Rename first 20 characters of every filename in a file - powershell

I am trying to write a script in powershell to remove the first 20 characters of every MP3 filename in a folder, I have created a file 'test.ps' and inserted the powershell code below into it,
gci *.mp3 | rename-item -newname { [string]($_.name).substring(20) }
When I run this file in powershell.exe nothing happens,
Can anyone help? Thanks.

This may get you started. (There are probably much more concise ways, but this works and is readable when you need to maintain it later. :-) )
I created a folder C:\TempFiles, and created the following files in that folder:
TestFile1.txt
TestFile2.txt
TestFile3.txt
TestFile4.txt
(I created them the old-fashioned way, I'm afraid. <g>. I used
for /l %i in (1,1,4) do echo "Testing" > TestFile%i.txt
from an actual command prompt.)
I then opened PowerShell ISE from the start menu, and ran this script. It creates an array ($files), containing only the names of the files, and processes each of them:
cd \TempFiles
$files = gci -name *.txt
foreach ($file in $files) {
$thename = $file.substring(4);
rename-item -path c:\TempFiles\$file -newname $thename
}
This left the folder containing:
File1.Txt
File2.Txt
File3.Txt
File4.Txt
File5.Txt
In order to run a script from the command line, you need to change some default Windows security settings. You can find out about them by using PowerShell ISE's help file (from the menu) and searching for about_scripts or by executing help about_scripts from the ISE prompt. See the sub-section How To Run A Script in the help file (it's much easier to read).

Your code actually works. Two things...
Rename the file to test.ps1.
Run it in the folder you have your MP3 files in. Since you didn't provided a path to Get-ChildItem it will run in the current directory.
I tested your line by making a bunch of mp3 files like this -
1..30 | % { new-item -itemtype file -Name (
$_.ToString().PadLeft(30, 'A') + ".mp3" )}

I would use a more "safer" way (you'll get an error if the file name is shorter than the length in question, you are also targeting the file extension as a part of the total characters). Check if the base name of each file is greater than 21 characters (if you remove the first 20 it can be still have a name with one character long). It can fail if the directory contains a file with same name after you removed the first 20, you can develop it further on your own):
gci *.mp3 | foreach{
if($_.BaseName.Length -ge 21)
{
$ext = $_.Extension
$BaseName = $_.BaseName.Substring(20)
Rename-Item $_ -NewName "$BaseName$ext"
}
}

// delete (replace with empty char) first 20 charters in all filename witch is started with "dbo."
// powershell
Get-ChildItem C:\my_dir\dbo -Recurse -Force -Filter dbo.* | Where-Object {!$_.PSIsContainer} | Rename-Item -NewName { ($_.name).Substring(20) }

Related

How can I modify this PowerShell script to include changing directory names to lower?

I am trying to rename all files and directories to lower and I found a powershell script here: Rename files to lowercase in Powershell
My favorite answer is the following because it is the cleanest and most concise answer. However, it does not include directory names and I don't have enough rep yet to respond to the comment
Get-ChildItem -r | Where-Object { !$_.PSIsContainer } | Rename-Item -NewName { $_.FullName.ToLower() }
I don't know PowerShell and I don't intend to become proficient, please skip all the details I'm just looking for code to rename all my files and directories to lower and I don't need to know anything about how it works. I don't like the following solution because 1, it is too wordy and 2, it only does directory names and not file names.
Where-Object { $_.PSIsContainer -And $_.Name -CMatch "[A-Z]" } |
ForEach-Object {
$NName = $_.Name.ToLowerInvariant()
# Set temporary name to enable rename to the same name; Windows is not case sensitive
$TempItem = Rename-Item -Path $_.FullName -NewName "x$NName" -PassThru
Rename-Item -Path $TempItem.FullName -NewName $NName
}
I want one clean command to rename files and directories, similar to the first example, please
when i first wrote this, i just wanted to open powershell and paste a command. in hindsight, that is not most efficient way either. so i ended up saving each script (one for files, one for folders) into one .ps1 file that you put in whatever directory you want to lower, then right-click and "run with powershell" and it will rename all files and subdirectories
the script looks like this:
# files to lower
Get-ChildItem -r | Where-Object { !$_.PSIsContainer } |
Rename-Item -NewName { $_.FullName.ToLower() }
# folders to lower
$fso = New-Object -ComObject Scripting.FileSystemObject
Get-ChildItem . -rec -dir |
ForEach-Object { $fso.MoveFolder($_.fullname, $_.Fullname.ToLower()) }
as you mentioned on your provided code, Windows is not a case sensitive OS, so you need to rename the directories to a temp name (for example insert a character after lowering it) then rename it again (by removing the inserted character)
i modified your line as follow to be able to lower both directories and files, please give it a try
Get-ChildItem -r | Rename-Item -NewName { $_.Name.ToLower().Insert(0,'_') } -PassThru | Rename-Item -NewName { $_.Name.Substring(1) }

PowerShell partial zip file name

Currently I have a code that extracts a zip file that is uploaded nightly and it has the name CallRecording_1-000-XXXXXXXX the X's represent the date and time that the zip file was generated. What I would like to do is have a powershell script that looks for the partial name. So for example it would look for just CallRecording_1-000 or CallRecording.
At the moment I have the following script:
#expand archive into folder
expand-archive ("Y:\CallRecording_1-000.zip") -destinationPath $folder
#rename zip file with yesterdays date
$yesDateName = $yesDate + ".zip"
Rename-Item "Y:\CallRecording_1-000.zip" -NewName $yesDateName
The scripts that I have found previously that use partial names seems to focus mostly on the extension rather than the name itself.
Any help would be appreciated!
It sounds like you only expect 1 zip file however I tailored this answer around the possibility of having more than 1
We are going to use Get-ChildItem to get any zip files from y:\ that match 'CallRecordings*.zip'
We then pipe these files one at a time to the ForEach-Object cmdlet where we
assign the extraction folder
unzip the file
and then rename the file.
$i is used to allow us different names for our renamed zip file in case there are more than 1 being processed.
$i = 0
Get-ChildItem -Path 'Y:\' -Filter 'CallRecording*.zip' | ForEach-Object -Process {
$extractFolder = "C:\temp\$($_.BaseName)"
$_ | Expand-Archive -DestinationPath $extractFolder
# ($? tells us if the last command completed successfully)
if ($?) {
# only rename file if Expand-Archive was successful
$_ | Rename-Item -NewName ((Get-Date).AddDays(-1).ToString('yyyyMMdd') + "_$((++$i)).zip")
}
}

How to prevent PowerShell -Recurse from renaming first file twice?

When using powershell to rename files with their directory name and file name, my code works, except in the first file in a directory, it gives it two copies of the directory name. So the file book1.xlsx in folder folder1 should become folder1book1.xlsx but it becomes folder1folder1book1.xlsx. The remaining files in folder1 are correctly named folder1book2.xlsx, folder1book3.xlsx, etc.
I have a directory, with many sub-directories. In each sub-dir are files that need their sub-dir name added in.
I've been following this code. For me it looks like:
dir -Filter *.xlsx -Recurse | Rename-Item -NewName {$_.Directory.Name + "_" + $_.Name}
I've also tried
--setting the Recurse -Depth 1 so that it doesn't keep looking for folders in the sub-folders.
--using ForEach-Object {$_ | ... after the pipe, similar to this.
--running it in Visual Studio Code rather than directly in PowerShell, which turns it into:
Get-ChildItem "C:\my\dir\here" -Filter *.xls -Recurse | Rename-Item -NewName {$_.DirectoryName + '_' + $_.Name}
--putting an empty folder inside the sub-directory, setting -Depth 2 to see if that will "catch" the recurse loop
I would expect the files to be named folder1_book1.xlsx, folder1_book2.xlsx, folder1_book3.xlsx.
But all of the attempted changes above give the same result. The first file is named folder1_folder1_book1.xlsx [INCORRECT], folder1_book2.xlsx[CORRECT], folder1_book3.xlsx[CORRECT].
A workaround might be writing an if statement for "not files that contain the sub-directory name" as suggested here. But the link searches for a text string not an object (probably not the correct term) like #_.Directory.Name. This post shows how to concatenate objects but not something like #_.Directory.Name. Having to put in an if statement seems like an unnecessary step if -Recurse worked the way it should, so I'm not sure this workaround gets at the heart of the issue.
I'm running windows 10 with bootcamp on a 2018 iMac (I'm in Windows a lot because I use ArcMap). Powershell 5.1.17134.858. Visual Studio Code 1.38.0. This is a task I would like to learn how to use more in the future, so explanations will help. I'm new to using PowerShell. Thanks in advance!
This was a script I created for one of my customers that may help
<##################################################################################################################################
This script can be used to search through folders to rename files from their
original name to "filename_foldername.extension". To use this script
please configure the items listed below.
Items to Congfigure
-$Original
-$Source
-$Destination
-$Files
Also please change the Out-File date on line 29 to today's date ****Example: 2019-10-02****
We've also added a change log file that is named "FileChange.txt" and can be found in the location identified on line 30
>
$Original="C:\temp\test" #Location of ".cab" files copied
$Source="C:\temp\Test" #Location were ".cab" files are stored
$Destination="C:\temp\Test\2019-10-02" #Location were you want to copy ".cab" files after the file name change. Be sure to change the date to the date you run this script. The script creates a folder with todays date
$Files=#("*.cab") #Choose the file type you want to search for
$ErrorActionPreference = "SilentlyContinue" #Suppress Errors
Get-ChildItem $Original -Include "*.cab" -File -Recurse | Rename-Item -NewName {$_.BaseName+"_"+$_.Directory.Name +'.cab'}
New-Item -ItemType Directory -Path ".\$((Get-Date).ToString('yyyy-MM-dd'))"; Get-ChildItem -recurse ($Source) -include ($Files) | Copy-Item -Destination ($Destination) -EA SilentlyContinue
Get-ChildItem $Original | Where {$_.LastWriteTime -ge [datetime]::Now.AddMinutes(-10)} | Out-File C:\temp\test\2019-10-02\FileChange.txt

Get Relative paths of all the folders, sub-folders and files in a folder

I have a folder named source. It's structure is like the following.
source\a.jpg
source\b.jpg
source\c.xml
source\d.ps1
source\subdir1\a.xml
source\subdir2\b.png
source\subdir3\subsubdir1\nothing.img
I want to list all the relative paths of folders, sub-folders and files in a text file say, out.txt. For above the output I expect is:
source\a.jpg
source\b.jpg
source\c.xml
source\d.ps1
source\subdir1\a.xml
source\subdir2\b.png
source\subdir3\subsubdir1\nothing.img
source\subdir1
source\subdir2
source\subdir3
source\subdir3\subsubdir1
You can see that the output includes individual folders and sub-folders too.
Note: I am in a folder just outside the source folder. I mean for example I am in fold folder which contains source folder -> fold/source but if your solution includes putting the script inside the source folder, thats fine too. Both solutions are fine. This may be easy but I am not familiar with powershell but can at least run scripts from it if given.
EDIT1: Okay, in the "duplicate question" the answer is for relative paths of individual files. But I also want the folders and sub-folders.
EDIT2: Okay, I wrote a command:
(gci -path source -recurse *.|Resolve-path -relative) -replace "","" | out-file -FilePath output.txt -Encoding ascii
Now, this command gives me the relative name of only subdirectory inside the source(in the actual source folder of mine with a different name; source is a dummy name obviously!). What should I change in this code to get other names of files inside the subdirectory in source.
This is clean one line answer,no loops on my part to write, which does the job perfectly. I produced it by luck "playing around" a bit.
(gci -Path source -Recurse *.*|Resolve-Path -Relative) -replace "\.","" |
Out-File -FilePath output.txt -Encoding ascii
Not very clean but this might be an option.
(Get-ChildItem -Recurse -Path "C:\ABC\source\").FullName|ForEach{[Regex]::Replace($_,'^C:\\ABC\\','')}
I would probably do something like this:
$source = 'C:\path\to\source'
$parent = [IO.Path]::GetDirectoryName($source) -replace '\\+$'
$pattern = '^' + [regex]::Escape($parent) + '\\'
Get-ChildItem -Path $source -Recurse | % { $_.FullName -replace $pattern }

Execute Powershell command from script path

First I will give a brief overview of what im trying to achieve. I want to go through a series of HTML files, replace code and then re-save these HTML files. This all works however the PS command will only execute this on HTML files which are on the default Powershell path (for me this is the H drive).
I want to be able to have a seperate folder which contains my powershell script and HTML files and convert them in that folder NOT from the H drive.
The code I have is follows:
Powershell script
$HTMLfiles=get-childitem . *.html -rec
foreach ($files in $HTMLfiles)
{
(Get-Content $files.PSPath) | ForEach-Object { $_ -replace "this text", "TEST" } | Set-Content $files.PSPath
}
This successfully changes all HTML files on the H drive that contain the words 'this text' with 'TEST'. I want to be able to change these HTML files from where the Powershell script is located, NOT from the H drive?
I appreciate any help.
Thanks
Use the built-in variable called $PSScriptRoot to retrieve the files from the same folder where the PowerShell script resides.
Get-ChildItem -Path $PSScriptRoot -Include *.HTML;
In your script, you ask to the Get-ChildItem cmdlet to look for items in the current directory, to make the script look for files in another directory, you just have to specify it to Get-ChildItem :
$HTMLpath="C:\path\to\your\html\files"
$HTMLfiles=get-childitem $HTMLpath *.html -rec
foreach ($files in $HTMLfiles)
{
(Get-Content $files.PSPath) | ForEach-Object { $_ -replace "this text", "TEST" } | Set-Content $files.PSPath
}
Edit :
if you want the path to be passed as an argument to your script, just do the following :
param($HTMLpath)
$HTMLfiles=get-childitem $HTMLpath *.html -rec
foreach ($files in $HTMLfiles)
{
(Get-Content $files.PSPath) | ForEach-Object { $_ -replace "this text", "TEST" } | Set-Content $files.PSPath
}
then you can call your script in the console (assuming you are in the directory where your script is) : ./myscript "C:\path\to\your\files"
Calling Get-ChildItem . *.html -Rec will get all files under the current working directory. If you happen to be in the same folder as your script when you call it, I'd expect it to work as you want. If you call the script from another path, e.g. by setting up a scheduled task to run powershell.exe <path_to_script> then it may not pick up the files you want. Maybe H: is the root of your Windows user profile?
As per other answers, using $PSScriptRoot or passing the path under which the .html files reside in a parameter would be good. To combine both, you can add a parameter to your script AND set the default value for that parameter to be $PSScriptRoot:
param($HTMLpath = $PSScriptRoot)
This will (1) allow you to specify a remote path if necessary and (2) otherwise default to the path where the script is saved.