Programmatically edit file names to remove leading text - powershell

I have a variety of files with names in the directory that look like this:
first_file_123456.jpg
5 * second_file_246531 (2).jpg
What I am looking to do is lay my hands on a PowerShell script that can take these files and rename them like this:
123456.jpg
246531 (2).jpg
I am looking to strip the last underscore and all text leading up to it to rename my files so they can match item numbers in my enterprise resource planning system. This system is much older (2004 technology) so automating from that side is out.
What i have tried to wire up so far and does not seem to work properly is as follows:
Get-ChildItem -Recurse -filter *_* | `
Foreach-Object {
$oldName = $_.Name
$pos = $oldName.LastIndexOf("_")
$newName = $oldName.Substring($pos + 1)
if (Test-Path $newName) {
# This is where I get lost - if it runs into a duplicate file name
# how can I make the name unique
}
#write-host $_.fullname
write-host $oldName renamed To: $newName | Out-File renamelog.txt
#rename-item $_.FullName -NewName $newName
}
I commented out the commands that actually do something to see what the output is.

Enumerate your files, filter for filenames containing an underscore, then rename them with everything up to and including the last underscore removed.
$re = '^.*_'
Get-ChildItem 'C:\some\folder' |
Where-Object { $_.Name -match $re } |
Rename-Item -NewName { $_.Name -replace $re }

Here's a short demo that uses the LastIndexOf and Substring methods:
$name = "first_file_123456.jpg"
$indexOfLastUnderscore = $name.LastIndexOf("_")
$newName = $name.Substring($indexOfLastUnderscore + 1, $name.Length - $indexOfLastUnderscore - 1)
# $newName now contains "123456.jpg"
Here's another way that uses PowerShell's -split operator and array indexing:
$name = "first_file_123456.jpg"
$newName = ($name -split '_')[-1]
$newName
# $newName now contains "123456.jpg"

Batch rename:
Get-Childitem -path $startDir -recurse |
where { ! $_.PSIsContainer } |
foreach {
$newName = Join-Path $_.Directory ($_.Name -replace '.*_', '');
Rename-Item $_.FullName $newName;
};

Related

Replacing only part of the file name in Powershell

I got a problem with powershell and some pdfs names
So there is a bunch of them:
2021_01_21_Aneks_nr2.pdf
2021_11_31_Aneks_nr3.pdf
2021_05_04_Aneks_nr4.pdf
I need to replace "_" with "-" but only near date.
Output should look like this:
2021-05-04_Aneks_nr4.pdf
I'm new to programming and couldn't find solution in internet.
I was using this code :
Get-Childitem | foreach { rename item $_ $_.Name.Replace("_","-") }
But it works on all "_" so I cant use it in this scenario.
Thanks for help! :)
Using the -replace operator instead of the dot net method you can use regex. And there you can specify to replace only underlines between digits:
Get-ChildItem |
ForEach-Object {
Rename-Item -Path $_.FullName -NewName $($_.Name -replace '(?<=\d)_(?=\d)', '-')
}
Here you go
#Loop through files
get-childitem [path] | ForEach-Object {
#match pattern
$null = $_.name -match '\d{4}_\d{2}_\d{2}'
#get matching pattern
$pattern = $matches[0]
#replace _ with -
$replace = $pattern -replace '_','-'
#build new name
$newName = $_.name -replace $pattern,$replace
#rename file
rename-item -Path $_.fullname -NewName $newName
}
It matches the current name of the file for the pattern '\d{4}_\d{2}_\d{2}', gets the matching string, replaces _ with - and finally build the new name by replacing the matching string with the updated version.

Adding leading zeros to file name strings using PowerShell

I'm new to using PowerShell and am wondering how to simply pad number strings contained in several similar file names to have the same number of digits in each file name.
That is, I have a folder containing these 300 files:
dummy name 1.txt through dummy name 300.txt
and would simply like rename the files that have less than 3 digits in them to all have exactly 3 digits, such as:
dummy name 001.txt through dummy name 300.txt
If your files were produced sequentially and you've got name1.txt
, name2.txt, etc.,
And you want name0001.txt, name0002.txt, etc.
You can do
$j=1;
foreach ($i in Get-ChildItem "./name?.txt" -Name) {
echo $i;
$new_name = "name000" + $j + ".txt";
Rename-Item -Path $i -NewName $new_name;
$j=$j+1;
}
... Then do it again, changing $j to 10 at the start, taking a 0 off, and so on for the hundreds.
Okay for a one off and if you can then fix what's producing the files in the first place.
#TessellatingHeckler gave good answer in the comments:
gci | ren -n {[regex]::replace($_.name, '\d+', {"$args".PadLeft(3, '0')})}
I would do it like this:
$i=0;
$nameLike='dummy name'
[int]$currentid=0
#RENAME TO REMOVE THE SPACE IN THE MIDDLE
gci -File -Path "./" | where{ $_.name -like "$nameLike*"} | %{
#remove space in the middle
$newname = $_.Name.Split(' ')[0] + $_.Name.Split(' ')[1]
#convert the name into the padding you want (3)
[int]::TryParse($_.Name.Split(' ')[2].Split('.')[0],[ref]$CurrentId) | Out-Null
$newname = $newname + $CurrentId.ToString("000") + $_.Extension
Rename-Item -Path $_.FullName -NewName $newname
}
Details in here:
https://medium.com/#josegabrielortegac/powershell-renaming-files-with-powershell-e9012573647a?sk=502148e039058d84fe34608d77cd1aa2
Try this:
1..300 | %{
$source = "dummy name $_.txt"
$target = ('dummy name {0:00#}.txt' -f $_)
rename-item $source -NewName $target
}
Notes:
The outer loop is a little unconventional. Most scripters code this to resemble a java for loop.
The choice of single and double quotes is intentional. Be careful.
The code is a little inelegant, because some parts of the filename are typed in twice.
The assumption is that all 300 files exist. If not, you will get errors.
try this:
Get-ChildItem "c:\temp\" -file -Filter "*.txt" | where basename -match ". \d{1,2}$" | %{
$res=$_.BaseName -split '(.)(\d{1,2})$'
$newnamme="{0}\{1} {2:D3}{3}" -f $_.Directory, $res[0], [int]$res[2], $_.Extension
Rename-Item $_.FullName $newnamme
}

Powershell renaming files recursively, not renaming duplicates

I have some powershell code that is meant to take specific characters out of file names recursively. This works great, unless taking the characters out causes 2 files to match names in the same folder.
I found the Powershell here PowerShell script to remove characters from files and folders but it doesn't solve the issue I am having.
$characters = "$#"
$regex = "[$([regex]::Escape($characters))]"
$filesandfolders = Get-ChildItem -recurse "D:\projects\filenameCleaner\TEST" | Where-Object {$_.name -match $regex}
$filesandfolders | Where-Object {!$_.PsIscontainer} | foreach {
$New=$_.name -Replace $regex
Rename-Item -path $_.Fullname -newname $New -passthru
}
$filesandfolders | Where-Object {$_.PsIscontainer} | foreach {
$New=$_.name -Replace $regex
Rename-Item -path $_.Fullname -newname $New -passthru
}
I'm not very versed in Powershell yet and would love some help with this. I just want the script that, if it finds a duplicate, it should just add a (1) or (2) etc at the end, depending on how many there are.
Please don't just give me an answer, explain it so that I can learn what is happening
I think the way to tackle this is to create a new PSOBJECT which contains your source and target paths. You can then use the Group-object to group on the target path which will give you a count which you can then use to determine which files will need a number suffix.
Something like this:
Get-ChildItem -recurse "D:\projects\filenameCleaner\TEST" | ? {!$_.PsIscontainer} | % {
New-Object psobject -Property #{
source = $_.FullName
target = ($_.FullName -replace [regex]::Escape($_.Name)) + ($_.Name -replace '[\$#]+', '')
}
} | Group-Object target | % {
$g = $_.Group
0..($g.Count - 1) | % {
New-Object psobject -Property #{
source = $g[$_].source
target = $g[$_].target -replace '\.', #('.', "($($_)).")[$_ -gt 0]
}
}
} | % {Rename-Item -Path $_.source -NewName $_.Target}
The nice thing about doing it this way is that it can two, three or more duplcates
I have only done the code for files so you would need another version for folders.
I was able to do this by adding an if-statement that checks the new path
$characters = "$#}"
$regex = "[$([regex]::Escape($characters))]"
$path = "D:\projects\filenameCleaner\TEST"
$filesandfolders = Get-ChildItem -recurse $path | Where-Object {$_.name -match $regex}
$filesandfolders | Where-Object {!$_.PsIscontainer} | foreach {
$New=$_.name -Replace $regex
$newPath = $path+"\"+$New
$loop = 0
while (Test-Path $newPath) {
$loop = $loop + 1
$basename = $_.basename -Replace $regex
$New = $basename + " ("+$loop+")"+$_.extension
$newPath = $path+"\"+$New
}
"Rename `""+$_.name+"`" to `""+$New+"`""
Rename-Item -path $_.Fullname -newname $New -passthru
}
$filesandfolders | Where-Object {$_.PsIscontainer} | foreach {
$New=$_.name -Replace $regex
$newPath = $path+"\"+$New
$loop = 0
while (Test-Path $newPath) {
$loop = $loop + 1
$basename = $_.basename -Replace $regex
$New = $basename + " ("+$loop+")"+$_.extension
$newPath = $path+"\"+$New
}
"Rename `""+$_.name+"`" to `""+$New+"`""
Rename-Item -path $_.Fullname -newname $New -passthru
}
-- Another update that handles the extensions separately when adding the number to the string for duplicates. I was having issues that the number would go onto the file name after the extension which is obviously a no-go

File Name Contains Certain Text

I know this is a new guy question, but I cant find anything. I think the issue might have to do with variable type issue?
I'm trying to look at a files name and see if pattern1 is contained in the file name. If so then replace the pattern text with "TEST".
Right now it doesn't error out, but it skips the IF, I do have files with the pattern in the directory.
Can't insert actually code, so here is a sample
$pattern1 = "January"
$pattern2 = "December 31"
$path = "C:\Users\...\METRICS-TEST\Metrics 2014\January 2014 Client Metrics"
$search_results = Get-ChildItem -Path $path | Where-Object { ((! $_.PSIsContainer))}
foreach ($file in $search_results) {
if($file.Name -contains $pattern1){
$new_name = $file.Name -replace $pattern1, "TEST1"
Rename-Item -Path $file.FullName -NewName $new_name
}else{
$new_name = $file.Name -replace $pattern2, "TEST2"
Rename-Item -Path $file.FullName -NewName $new_name
}
}
-contains work on collections, not strings.
For strings, you'd want either -match (for RegEx matching) or -like for simple wildcard matching:
$file.name -match $pattern1
$file.name -like "*$pattern1*"
-contains would be appropriate if you had an array of strings and wanted to know if it contained one or more copies of a specific string:
$Strings = "abc","def","ghi","jkl"
# This evaluates to true
$Strings -contains "abc"
# This evaluates to false
$Strings -contains "ab"
The whole issue is that you use -Contains where you should use -Match. Try this though, it's easily expandable if you have more conditions to add:
$pattern1 = "January"
$pattern2 = "December 31"
$path = "C:\Users\...\METRICS-TEST\Metrics 2014\January 2014 Client Metrics"
Switch(Get-ChildItem -Path $path){
{$_.Name -match $pattern1}{$new_name = $_.Name -replace $pattern1, "TEST1"
Rename-Item -Path $_.FullName -NewName $new_name}
{$_.Name -match $pattern2}{$new_name = $_.Name -replace $pattern2, "TEST2"
Rename-Item -Path $_.FullName -NewName $new_name}
}
I realize I have an accepted answer, but I thought another elegant way to handle this would be mapping Old-To-New in a hashtable, matching against the keys of that hashtable (regex escaped and joined with pipes to form the regex match pattern), and then renaming in a ForEach against the matches rather than a switch. I suppose at this point it's all academic.
$path = "C:\Users\...\METRICS-TEST\Metrics 2014\January 2014 Client Metrics"
$RenameMap = #{
"January" = "Test1"
"December 31" = "Test2"
}
$Pattern = "($(($RenameMap.keys|ForEach{[regex]::Escape($_)}) -join "|"))"
Get-ChildItem $Path | Where{$_.Name -match $Pattern} | ForEach {Rename-Item $_.FullName -NewName ($_.Name -Replace $Matches[1],$RenameMap[$Matches[1]])}

Renaming a file that has a bracket in the file name using power shell

I am trying to rename a file that has a bracket in the file name. This does not seem to work because powershell sees [] as special characters and does not know what to do.
I have a folder on my computer c:\test. I want to be able to look through that folder and rename all files or portions of the file. The following code seems to work but if the file has any special characters in it the code fails:
Function RenameFiles($FilesToRename,$OldName,$NewName){
$FileListArray = #()
Foreach($file in Get-ChildItem $FilesToRename -Force -Recurse | Where-Object {$_.attributes -notlike "Directory"})
{
$FileListArray += ,#($file)
}
Foreach($File in $FileListArray)
{
IF ($File -match $OldName )
{
$File | rename-item -newName {$_ -replace "$OldName", "$NewName" }
}
}
}
renamefiles -FilesToRename "c:\test" -OldName "testt2bt" -NewName "test"
I did find a similar question: Replace square bracket using Powershell, but I can't understand how to use the answer cause it's just a link explaining the bug:
For multiple files this can be done with one line.
To remove the bracket you should try:
get-childitem | ForEach-Object { Move-Item -LiteralPath $_.name $_.name.Replace("[","")}
Move-Item -literalpath "D:\[Copy].log" -destination "D:\WithoutBracket.txt"
Use the literalpath switch with the Move-Item cmdlet [instead of using the rename-item cmdlet]
As far as bracket are concerned, you've got Microsoft official answer in an old Technet Windows PowerShell Tip of the Week.
You can use :
Get-ChildItem 'c:\test\``[*``].*'
Thanks for help guys you all helped a lot this is the solution I came up with in the end after reading your reply’s .
I have a folder on my pc called c:\test and it has a file in it called "[abc] testfile [xas].txt" and i want it to be called testfile2.txt
Function RenameFiles($FilesToRename,$OldName,$NewName){
$FileListArray = #()
Foreach($file in Get-ChildItem $FilesToRename -Force -Recurse | Where-Object {$_.attributes -notlike "Directory"})
{
$FileListArray += ,#($file.name,$file.fullname)
}
Foreach($File in $FileListArray)
{
IF ($File -match $OldName )
{
$FileName = $File[0]
$FilePath = $File[1]
$SName = $File[0] -replace "[^\w\.#-]", " "
$SName = $SName -creplace '(?m)(?:[ \t]*(\.)|^[ \t]+)[ \t]*', '$1'
$NewDestination = $FilePath.Substring(0,$FilePath.Length -$FileName.Length)
$NewNameDestination = "$NewDestination$SName"
$NewNameDestination | Write-Host
Move-Item -LiteralPath $file[1] -Destination $NewNameDestination
$NewNameDestination | rename-item -newName {$_ -replace "$OldName", "$NewName" }
}
}
}
renamefiles -FilesToRename "c:\test" -OldName "testfile" -NewName "testfile2"