How to batch rename files in PowerShell starting with padded first numbers - powershell

I'm a PowerShell newbie so please be gentle! I'm looking to bulk rename a collection of files, then pad the first digits so that they play in order on my devices.
For example, I have a folder full of mp3s ABCDEF_S3L1_010221_GHIJK.mp3 through ABCDEF_S3L49_210921_GHIJK.mp3
I'm trying to rename them as 01-lesson.mp3 through 49-lesson.mp3 where the digit after the L is the prefix but I trip up with the padding for lessons 1-9 so they play out of order, i.e. 01 then 10, 11, 12 etc. I've tried using variations of .PadLeft and {0:D2} but keep getting errors such as:
Rename-Item: The filename, directory name, or volume label syntax is incorrect.
My code works without the initial padding:
PS G:\Temp\MP3s> Get-ChildItem -Path *.mp3 | Rename-Item
-NewName {$_.Name -replace '(^ABCDEF_S3L)(\d{1,2})_(\d{6})_(GHIJK)', '$2-lesson'}`

You can use -match and by doing so capture the number behind the L in $matches[1], which can be formatted like you want:
Get-ChildItem -Path 'D:\Test' -Filter '*.mp3' -File |
# apply better filename filter and capture the number after the 'L' in $matches[1]
Where-Object { $_.BaseName -match '^\w+_S\dL(\d+)_\d+_\w+' } |
Rename-Item -NewName { ('{0:D2}-lesson{1}' -f [int]$matches[1], $_.Extension) }

Cheating and going to powershell 7 with the scriptblock replace. You just have to workout where the 2nd group match is within the $_ match object, which turns out to be $_.groups[2].value. Then you can use .padleft(2,'0') on it.
# echo hi | set-content ABCDEF_S3L1_010221_GHIJK.mp3,
# ABCDEF_S3L49_210921_GHIJK.mp3
Get-ChildItem -Path *.mp3 |
% { $_.Name -replace '(^ABCDEF_S3L)(\d{1,2})_(\d{6})_(GHIJK)',
{ $a = $_ } }
.mp3
.mp3
$a
Groups : {0, 1, 2, 3, 4}
Success : True
Name : 0
Captures : {0}
Index : 0
Length : 25
Value : ABCDEF_S3L49_210921_GHIJK
Get-ChildItem -Path *.mp3 |
rename-item -newname {
$_.Name -replace '(^ABCDEF_S3L)(\d{1,2})_(\d{6})_(GHIJK)',
{ $_.groups[2].value.padleft(2,'0') + '-lesson' } } -whatif
What if: Performing the operation "Rename File" on target
"Item: C:\Users\js\foo\ABCDEF_S3L1_010221_GHIJK.mp3
Destination: C:\Users\js\foo\01-lesson.mp3".
What if: Performing the operation "Rename File" on target
"Item: C:\Users\js\foo\ABCDEF_S3L49_210921_GHIJK.mp3
Destination: C:\Users\js\foo\49-lesson.mp3".

Related

How to add a symbol in a middle of a file name in PS

I have modified several thousand files with a various things requested by the owners.
Now, I need to add one final thing and I am not 100% on how to do it.
Scenario is as follows - all files have a 10 digit number at the start, I need to add a hyphen after the number. String is a variable but it is always the same length.
1234567890abcdefgh.xls would be an example
I have used GCI to make changes to symbols and static parts but not sure how to call for insertion in a specific place of a file (after the 10th character of a variable string)
Any ideas would be most welcome!
You can use the $matches you get from the capturing groups of a -match comparison:
(Get-ChildItem -Path 'X:\WhereTheFilesAre' -File) |
Where-Object { $_.BaseName -match '^(\d{10})([^-].*)' } |
Rename-Item -NewName { '{0}-{1}{2}' -f $matches[1], $matches[2], $_.Extension }
or by using the Substring() method:
(Get-ChildItem -Path 'X:\WhereTheFilesAre' -File) |
Where-Object { $_.BaseName -match '^\d{10}[^-]' } |
Rename-Item -NewName { $_.Name.Substring(0,10) + '-' + $_.Name.Substring(10) }
or use the regex -replace operator:
(Get-ChildItem -Path 'X:\WhereTheFilesAre' -File) |
Where-Object { $_.BaseName -match '^\d{10}[^-]' } |
Rename-Item -NewName { $_.Name -replace '^(\d{10})', '$1-' }
You can use string.Insert() to insert a string into another at a specific offset:
PS ~> '1234567890abcdefgh.xls'.Insert(10, '-')
1234567890-abcdefgh.xls
To apply to all files in a directory, you could do something like this:
Get-ChildItem -File |Where-Object Name -match '^\d{10}[^-]' |Rename-Item -NewName { $_.Name.Insert(10, '-') }
The regular expression pattern ^\d{10}[^-] will only match file names that start with 10 digits followed by something other than a hyphen (to avoid renaming files that already comply with the naming convention)

How can I use powershell to rename files with padded zeros

I need to limit the files affected to Example*.pdf. The files have 1-3 digits in the names and I need to standardize them. So Example_1.pdf -> Example_001.pdf while Example_100.pdf -> Example_100.pdf
The first part renamed files to Example.1.pdf so I could parse them with a single delimiter, but it gave me errors on the second step (cmdlet rename-item at command pipeline position 1 supply values for the following parameters: path)
Get-ChildItem of* | rename-item -newname { $_.Name -replace '_','.' }
Get-ChildItem of* |
foreach {
$nameArray = $_.Split('.')
$ExampleNumber = $nameArray[1]
rename-item -path $Path -newname $nameArray[0]+$ExampleNumber+$nameArray[2]
}
But if I can get something like this to work then I can play around with $ExampleNumber
Then I tried using regular expressions. Had this worked it would have padded the single digit files and then I could make a second pass for double digit files. But it didn't rename anything.
Get-ChildItem ex* | rename-item -newname { $_ -replace '(.*)(\d{1})\.pdf', 'Example_0$2.pdf'}
Any help is appreciated.
Note the $_ inside the -NewName block is of type FileInfo.
Here's my suggestion:
Get-ChildItem ex* | Rename-Item -NewName {
[void]($_.Name -match "\d+")
$_.Name -replace "\d+", (([int]$Matches[0]).ToString("000"))
}
Or alternatively:
Get-ChildItem ex* |
Rename-Item -NewName {
$_.Name -replace "\d+", (([int][regex]::match($_.Name, "\d+").value).ToString("000"))}

Correction in sub folder names by replacing first two characters, if needed

I am using below Powershell script which successfully traverses through all my case folders within the main folder named Test. What it is incapable of doing is to rename each sub folder, if required, as can be seen in current and desired output. Script should first sort the sub folders based on current numbering and then give them proper serial numbers as folder name prefix by replacing undesired serial numbers.
I have hundreds of such cases and their sub folders which need to be renamed properly.
The below output shows two folders named "352" and "451" (take them as order IDs for now) and each of these folders have some sub-folders with a 2 digit prefix in their names. But as you can notice they are not properly serialized.
$Search = Get-ChildItem -Path "C:\Users\User\Desktop\test" -Filter "??-*" -Recurse -Directory | Select-Object -ExpandProperty FullName
$Search | Set-Content -Path 'C:\Users\User\Desktop\result.txt'
Below is my current output:
C:\Users\User\Desktop\test\Case-352\02-Proceedings
C:\Users\User\Desktop\test\Case-352\09-Corporate
C:\Users\User\Desktop\test\Case-352\18-Notices
C:\Users\User\Desktop\test\Case-451\01-Contract
C:\Users\User\Desktop\test\Case-451\03-Application
C:\Users\User\Desktop\test\Case-451\09-Case Study
C:\Users\User\Desktop\test\Case-451\14-Violations
C:\Users\User\Desktop\test\Case-451\21-Verdict
My desired output is as follows:
C:\Users\User\Desktop\test\Case-352\01-Proceedings
C:\Users\User\Desktop\test\Case-352\02-Corporate
C:\Users\User\Desktop\test\Case-352\03-Notices
C:\Users\User\Desktop\test\Case-451\01-Contract
C:\Users\User\Desktop\test\Case-451\02-Application
C:\Users\User\Desktop\test\Case-451\03-Case Study
C:\Users\User\Desktop\test\Case-451\04-Violations
C:\Users\User\Desktop\test\Case-451\05-Verdict
Thank you so much. If my desired functionality can be extended to this script, it will be of great help.
Syed
You can do the following based on what you have posted:
$CurrentParent = $null
$Search = Get-ChildItem -Path "C:\Users\User\Desktop\test" -Filter '??-*' -Recurse -Directory | Where Name -match '^\d\d-\D' | Foreach-Object {
if ($_.Parent.Name -eq $CurrentParent) {
$Increment++
} else {
$CurrentParent = $_.Parent.Name
$Increment = 1
}
$CurrentNumber = "{0:d2}" -f $Increment
Join-Path $_.Parent.FullName ($_.Name -replace '^\d\d',$CurrentNumber)
}
$Search | Set-Content -Path 'C:\Users\User\Desktop\result.txt'
I added Where to filter more granularly beyond what -Filter allows.
-match and -replace both use regex to perform the matching. \d is a digit. \D is a non-digit. ^ matches the position at the beginning of the string.
The string format operator -f is used to maintain the 2-digit requirement. If you happen to reach 3-digit numbers, then 3 digit numbers will be output instead.
You can take this further to perform a rename operation:
$CurrentParent = $null
Get-ChildItem . -Filter '??-*' -Recurse -Directory | Where Name -match '^\d\d-\D' | Foreach-Object {
if ($_.Parent.Name -eq $CurrentParent) {
$Increment++
} else {
$CurrentParent = $_.Parent.Name
$Increment = 1
}
$CurrentNumber = "{0:d2}" -f $Increment
$NewName = $_.Name -replace '^\d\d',$CurrentNumber
$_ | Where Name -ne $NewName | Rename-Item -NewName $NewName -WhatIf
}
$NewName is used to simply check if the new name already exists. If it does, a rename will not happen for that object. Remove the -WhatIf if you are happy with the results.

Rename-Item with SubString/lastIndexOf

I'm trying to rename about 500 files in a single directory. Each file is .docx format similar to the below.
Apartment_7-9_01_92.docx
Apartment_7-9_02_192.docx
etc.
I want to remove the last two/three digits before the '.', including the '_' so that I end up with
Apartment_7-9_01.docx
Apartment_7-9_02.docx
Having never really used Powershell, the research I've done so far leads me to something like the below:
Get-ChildItem -File | ForEach-Object { $_ | Rename-Item -NewName -Replace $_.Name.SubString(0, $_.lastIndexOf('_')),".docx"}
I would have thought this would take everything, including and after the last '_' and replace it with '.docx', but it's telling me the lastIndexOf method doesn't exist in this case.
Thanks
I fixed your code, but I don't think it does what you want. $_ is a fileinfo object, and $_.name is a string, and .lastindexof() is a string method. I think you want to replace a substring location after the last "_", not before it.
Get-ChildItem -File | ForEach-Object { $_ | Rename-Item -NewName (
$_.name -Replace $_.Name.SubString(0, $_.Name.lastIndexOf('_')),".docx") -whatif}
What if: Performing the operation "Rename File" on target "Item:
/Users/js/Apartment_7-9_01_92.docx Destination: /Users/js/.docx_92.docx".
What if: Performing the operation "Rename File" on target "Item:
/Users/js/Apartment_7-9_02_192.docx Destination: /Users/js/.docx_192.docx".
This seems to work, and is close to what you were trying. Just specify where the substring starts.
Get-ChildItem -File | ForEach-Object { $_ | Rename-Item -NewName (
$_.name -Replace $_.Name.SubString($_.Name.lastIndexOf('_')),".docx") -whatif}
What if: Performing the operation "Rename File" on target "Item:
/Users/js/Apartment_7-9_01_92.docx Destination:
/Users/js/Apartment_7-9_01.docx".
What if: Performing the operation "Rename File" on target "Item:
/Users/js/Apartment_7-9_02_192.docx Destination:
/Users/js/Apartment_7-9_02.docx".
There's a way to pipe directly to rename-item too, like in the docs:
Get-ChildItem -File | Rename-Item -NewName {
$_.name -Replace $_.Name.SubString($_.Name.lastIndexOf('_')),".docx"} -whatif
You could first split the BaseName on "_", then take every item except the last and rejoin back on "_". An easy way to do that is with $array[0..($array.Length - 2)]. You could also just do $array[0..2] to take the first 3 items. You can then add the Extension at the end. From here you can simply rename the FullName with Rename-Item.
$Path = "PATH/TO/FILES"
Get-ChildItem -Path $Path -File | ForEach-Object {
$items = $_.BaseName -split "_"
$newFileName = ($items[0..($items.Length - 2)] -join "_") + $_.Extension
Rename-Item -Path $_.FullName -NewName $newFileName
}

Renaming a range from files w/Powershell - from first to third from last hyphen

I have a bunch of csv files I would like to rename with powershell.
from
abc-def-ghi-jkl-mno-pqr-ke-traffic-by-domains-17-Oct-2018_23-49-38-6d73395f476ad09a7506dc00533933b8.csv
abc-def-ghi-jkl-mno-pqr-ke-traffic-by-domains-15-Oct-2018_05-20-36-75eabae7c4123198ff5fe6f4f642449f.csv
abc-def-ghi-jkl-mno-pqr-ke-traffic-by-domains-12-Oct-2018_06-23-58-b13eb8f362c09fbe405458fac8af8f8e.csv
to
abc-17-Oct-2018.csv
abc-15-Oct-2018.csv
abc-12-Oct-2018.csv
I could delete the characters after the underscore (_) with this command
Get-ChildItem -Filter *.csv | Foreach-Object -Process {
$NewName = [Regex]::Match($_.Name,"^[^_]*").Value + '.csv'
$_ | Rename-Item -NewName $NewName}
which leads me to this file name
abc-def-ghi-jkl-mno-pqr-ke-traffic-by-domains-17-Oct-2018.csv
but I am lost at deleting the range of characters from the first hyphen to the third from last hyphen.
I tried this but get an error.
Get-ChildItem -Filter *.csv | Rename-Item -NewName {
-replace '^[^-]..^[^-]{-3}','^[^-]'}
Could somebody kindly enlighten me how to erase a range? (and possibly combine the former command)
Assuming that all input filenames have the same number of tokens with the same separators in the same positions:
Get-ChildItem -Filter *.csv | Rename-Item -NewName {
(($_.Name -split '[-_]')[0, 10, 11, 12] -join '-') + '.csv'
} -WhatIf
-WhatIf previews the renaming operations; remove it to perform actual renaming.
Splitting the filename into tokens by separators avoids complex regexes; PowerShell's flexible array slicing makes it easy to piece together the target filename from the tokens of interest accessed by their indices.
That said, if you wanted to do it with -replace and a complex regex:
Get-ChildItem -Filter *.csv | Rename-Item -NewName {
$_.Name -replace '^([^-]+).*?-(\d[^_]+).*', '$1-$2.csv'
} -Whatif
This solution doesn't assume a fixed position of the 2nd token to extract - the one before _ - and instead identifies its start by a - followed by a digit (\d).
So I found a very long and convoluted way to do it, but it works so it works.
# Adds files into an object
$CSV = Get-ChildItem "C:\temp\test\*.csv"
# Create a loop to action each file in the object created above
Foreach($File in $CSV){
# Splits each part of the filename using the hyphen
$NewName = #($File.basename.Split('-'))
# created a new name using each individual part of the split original name
# Also replaced the underscore section
$NewFileName = "$($NewName[0])"+"-"+"$($NewName[10])"+"-"+"$($NewName[11])"+"-"+"$($NewName[12] -replace '_.*')"+".csv"
# Renames file
Rename-Item $File $NewFileName
}
How about extract only what you need for the rename. Meaning What I tested. RegEx to trap first 4, date, and last 4. How I tested this approach.
Clear-Host
Write-Host "`nCreate the the file set *********" -ForegroundColor Cyan
'abc-def-ghi-jkl-mno-pqr-ke-traffic-by-domains-17-Oct-2018_23-49-38-6d73395f476ad09a7506dc00533933b8.csv',
'abc-def-ghi-jkl-mno-pqr-ke-traffic-by-domains-15-Oct-2018_05-20-36-75eabae7c4123198ff5fe6f4f642449f.csv',
'abc-def-ghi-jkl-mno-pqr-ke-traffic-by-domains-12-Oct-2018_06-23-58-b13eb8f362c09fbe405458fac8af8f8e.csv' |
%{New-Item -Path 'D:\Temp' -Name $_ -ItemType File}
Write-Host "`nValidate the file set creation *********" -ForegroundColor Cyan
(Get-ChildItem -Path 'D:\Temp' -Filter 'abc*').FullName
Write-Host "`nRead the folder for the file set and rename based on regex to shorten the name *********" -ForegroundColor Cyan
Get-ChildItem -Path 'D:\Temp' -Filter 'abc*' |
%{ Rename-Item -Path $_.FullName -NewName ([regex]::Matches($_.Name,'^.{0,4}|\d{2}.*\-\d{4}|.{4}$').Value -join '')}
Write-Host "`nValidate the name change *********" -ForegroundColor Cyan
(Get-ChildItem -Path 'D:\Temp' -Filter 'abc*').FullName
# Results
Create the the file set *********
Directory: D:\Temp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 10/18/2018 9:29 PM 0 abc-def-ghi-jkl-mno-pqr-ke-traffic-by-domains-17-Oct-2018_23-49-38-6d73395f476ad09a7506dc00533933b8.csv
-a---- 10/18/2018 9:29 PM 0 abc-def-ghi-jkl-mno-pqr-ke-traffic-by-domains-15-Oct-2018_05-20-36-75eabae7c4123198ff5fe6f4f642449f.csv
-a---- 10/18/2018 9:29 PM 0 abc-def-ghi-jkl-mno-pqr-ke-traffic-by-domains-12-Oct-2018_06-23-58-b13eb8f362c09fbe405458fac8af8f8e.csv
Validate the file set creation *********
D:\Temp\abc-def-ghi-jkl-mno-pqr-ke-traffic-by-domains-12-Oct-2018_06-23-58-b13eb8f362c09fbe405458fac8af8f8e.csv
D:\Temp\abc-def-ghi-jkl-mno-pqr-ke-traffic-by-domains-15-Oct-2018_05-20-36-75eabae7c4123198ff5fe6f4f642449f.csv
D:\Temp\abc-def-ghi-jkl-mno-pqr-ke-traffic-by-domains-17-Oct-2018_23-49-38-6d73395f476ad09a7506dc00533933b8.csv
Read the folder for the file set and rename based on regex to shorten the name *********
Validate the name change *********
D:\Temp\abc-12-Oct-2018.csv
D:\Temp\abc-15-Oct-2018.csv
D:\Temp\abc-17-Oct-2018.csv