Bulk rename files in powershell and add a count to each filename - powershell

I am able to bulk rename files in a directory OR substitute each file name with a count, however I need both combined.
Bulk rename (keep first 2 charachters of original name):
Get-ChildItem * |
Rename-Item -NewName { $_.BaseName.Split('-')[0] + $_.Extension }
(partially hard coded solution, I know!)
Alternatively, add count:
$count = 1
Get-ChildItem * | % { Rename-Item $_ -NewName (‘{0}.xlsx’ -f $count++) }
(I am not even dreaming about trailing 0s)
I tried to combine both things, but to no avail. What am I doing wrong?
My failed attempt:
$count = 1
Get-ChildItem * |
Rename-Item -NewName { $_.BaseName.Split('-')[0] -f $count++ + $_.Extension }

You're misunderstanding how the format operator works. You need a format string with a placeholder ({0}) in order to make that operator work. I would also recommend putting grouping parentheses around that expression, even though that shouldn't be required in this case. Just to be on the safe side.
('foo {0} bar' -f $some_var) + $other_var
With that said, you apparently want to append the value of the counter variable to the fragment from the original file name rather than using that fragment as a format string. For that you can simply concatenate the counter to the fragment, just like you do with the extension.
For the counter to work correctly you also need to specify the correct scope. The way you're using it defines a new variable $counter in the local scope of the scriptblock every time a file is renamed, so the variable $counter in the parent scope is never incremented. Use the script: or global: scope modifier to get the variable you actually intend to use.
$count = 1
Get-ChildItem * |
Rename-Item -NewName { $_.BaseName.Split('-')[0] + $script:count++ + $_.Extension }
If you do want to use the format operator instead of string concatenation (+) you'd use it like this:
$count = 1
Get-ChildItem * |
Rename-Item -NewName { '{0}{1}{2}' -f $_.BaseName.Split('-')[0], $script:count++, $_.Extension }

To have the $count counted up, you have to go with the loop.
$count = 1
Get-ChildItem * | foreach-object { Rename-Item -NewName { $_.BaseName.Split('-')[0] -f
count++ + $_.Extension } }
% is the alias for the foreach-object cmdlet you used in the "add count" approach.
Just added it to you failed attempt.

Related

Rename a sequence of files to a new sequence, keeping the suffix

I need help to create a command in PowerShell to rename a sequence of files with this format:
001.jpg
001_1.jpg
002.jpg
002_1.jpg
003.jpg
003_1.jpg
into a new sequence that can start with a number such as 9612449, but keeping intact the suffixes, so new sequence would be:
9612449.jpg
9612449_1.jpg
9612450.jpg
9612450_1.jpg
9612451.jpg
9612451_1.jpg
Assuming that 9612449 is an offset to be added to the existing numbers that make up the first _-separated token or all of the base file names:
# Simulate a set of input files.
$files = [System.IO.FileInfo[]] (
'001.jpg',
'001_1.jpg',
'002.jpg',
'002_1.jpg',
'003.jpg',
'003_1.jpg'
)
# The number to offset the existing numbers by.
$offset = 9612449 - 1
# Process each file and apply the offset.
$files | ForEach-Object {
# Split the base name into the number and the suffix.
$num, $suffix = $_.BaseName -split '(?=_)', 2
# Construct and output the new name, with the offset applied.
'{0}{1}{2}' -f ($offset + $num), $suffix, $_.Extension
}
The above yields the output shown in your question.
Applied to a real file-renaming operation, you'd do something like:
Get-ChildItem -LiteralPath . -Filter *.jpg |
Rename-Item -NewName {
$num, $suffix = $_.BaseName -split '(?=_)', 2
'{0}{1}{2}' -f ($offset + $num), $suffix, $_.Extension
} -WhatIf
Note: The -WhatIf common parameter in the command above previews the operation. Remove -WhatIf once you're sure the operation will do what you want.

Powershell - Rename files preserving part of the name + checksum

I have some long filenames that potensially can cause issues with Windows path max chars, and I would like to rename them preserving part of it - and also adding a RNG 4 letter/digit combination.
Filename example: 478432_1400_79834_SomeKindofText_UserInputSoItCanBeReallyLongCombinedWithANetworkPath.jpg
Wanted rename outcome:
478432_1400_79834_SomeKindofText_abc1.jpg
Where 'abc1' represents the 4 letter/digit combination of a checksum
This is the code I have so far:
$search_folder = "C:\PS\Test\"
Get-ChildItem $search_folder -File | ForEach-Object {
$checksum = Get-FileHash -Path $_
$checksum = $checksum.substring(0,3)
Rename-Item -NewName { $search_folder+$_.BaseName.Split('_')[0..3] + $checksum + $_.Extension }
}
My first problem is that Get-FileHash does not support substring method, generating a error message:
Method invocation failed because [Microsoft.Powershell.Utility.FileHash] does not contain a method named 'substring'.
My second problem is that it tries to do a Resolve-Path in my current PS shell directory instead of $search_folder
My third problem is that the underscores in the filename is not preserved, so a -WhatIf tag on the Rename-Item method yields a result like "478432 1400 79834 SomeKindofText"
Tips or suggestions would be most welcomed!
My first problem is that Get-FileHash does not support substring method, generating a error message:
Method invocation failed because [Microsoft.Powershell.Utility.FileHash] does not contain a method named 'substring'.
$checksum does not store the hash string, it stores an object that has a property named Hash, which in turn stores the string you want, so change this line:
$checksum = $checksum.substring(0,3)
To:
$checksum = $checksum.Hash.Substring(0,3)
My second problem is that it tries to do a Resolve-Path in my current PS shell directory instead of $search_folder
Two general solutions to this problem:
Pass the absolute path to the file explicitly:
Rename-Item -LiteralPath $_.FullName -NewName { ... }
Or pipe the output from Get-ChildItem directly to Rename-Item and let PowerShell bind the path correctly:
$_ |Rename-Item -NewName { ... }
My third problem is that the underscores in the filename is not preserved, so a -WhatIf tag on the Rename-Item method yields a result like "478432 1400 79834 SomeKindofText"
Splitting a string on '_' will also remove the underscores - to reverse this, use '_' as a delimiter in a -join operation:
$firstFourPartsOfBaseName = $_.BaseName.Split('_')[0..3] -join '_'
Putting this all together, we get:
$search_folder = "C:\PS\Test\"
Get-ChildItem $search_folder -File | ForEach-Object {
$checksum = Get-FileHash -Path $_
$checksum = $checksum.hash.substring(0,3)
$_ |Rename-Item -NewName {
# calculate new base name ("478432_1400_79834_SomeKindofText_abc1")
$newBasename = #($_.BaseName.Split('_')[0..3]; $checksum) -join ''
# add extension and output
$newBasename,$_.Extension -join '.'
}
}
Please see your script adjusted below:
$search_folder = "C:\PS\Test\"
Get-ChildItem $search_folder -File | ForEach-Object {
$checksum = Get-FileHash -Path $_
$checksum = $checksum.hash.substring(0,3)
Rename-Item -Path $_ -NewName ($search_folder + $_.BaseName.Split('_')[0..3] + $checksum + $_.Extension)
}
You were trying to get a sub string of an object, using the property hash on $Checksum will allow you to create a substring.
I have also added -path to the rename-item request and changed the parenthesis on the string construction (it could be either of these that were causing you an issue, unfortunately I haven't tested this.)

Powershell file rename syntax

I'm trying to rename all the files in a given directory so that they have the sequential format 001a.jpg, 001b.jpg, 002a.jpg etc.
I have tried:
$c=1
get-childitem * | foreach {
if($c/2=[int]($c/2)){
rename-item -NewName {($c-1) + 'b.jpg'} else rename-item - NewName {$c + 'a.jpg'}}$c++} - whatif
But I am getting "the assignment operator is not valid".
The = in the third line is obviously not supposed to be an assignment operator. I just want to check whether the counter is on an even number. I found -ne for not equal to but PS didn't seem to like the -.
I am not sure about the syntax of the new names but the parser doesn't get that far ATM.
Grateful for any pointers.
Edit: I've just realised the numbers would skip but I can fix that once I understand the problem with what I already have.
As commented, if you do not leave spaces around the -eq or -ne operators, PowerShell will interpret the dash as belonging to the math, so
if($c/2-ne[int]($c/2))
Will fail.
Also, to test if a number is odd, it is probably quicker to do if ($c % 2) or if ($c -band 1).
Use -not to test for even numbers.
Several things you need to know (some are just recommendations):
Every if-else branch requires curly brackets
If you do not input the items to Rename-Item via the pipeline, you have to specify the path
If you specify the path for Rename-Item, you cannot use a script block for -NewName
You cannot use + if the left operand is int and the right is string, because it would be interpreted as addition and requires two numbers.
You cannot use the -WhatIf switch on the whole construct, but only individual commands
It will suffice to use the command only once, and use the if-else only to set the new name
You can simplify your condition using the modulus operator % (as explained in other answers and comments)
You can use the -Begin parameter for initialization
The * for Get-ChildItem is not necessary
Here is the updated code:
Get-ChildItem | foreach -Begin { $c = 1 } -Process {
if ($c % 2) {
$newName = "${c}a.jpg"
}
else {
$newName = "$($c-1)b.jpg"
}
Rename-Item -LiteralPath $_.FullName -NewName $newName -WhatIf
$c++
}
Here is an even shorter version, that pipes directly into Rename-Item
$c = 0
Get-ChildItem |
Rename-Item -NewName {if (++$script:c % 2) {"${c}a.jpg"} else {"$($c-1)b.jpg"}} -WhatIf

Need to batch add characters to filenames using Powershell

I have a series of files all named something like:
PRT14_WD_14220000_1.jpg
I need to add two zeroes after the last underscore and before the number so it looks like PRT14_WD_14220000_001.jpg
I've tried"
(dir) | rename-Item -new { $_.name -replace '*_*_*_','*_*_*_00' }
Appreciate any help.
The closest thing to what you attempted would be this. In regex, the wildcard is .*. And the parentheses do grouping to refer to later with the dollar sign numbers.
dir *.jpg | rename-Item -new { $_.name -replace '(.*)_(.*)_(.*)_','$1_$2_$3_00' } -whatif
What if: Performing the operation "Rename File" on target "Item: C:\users\admin\foo\PRT14_WD_14220000_1.jpg Destination: C:\users\admin\foo\PRT14_WD_14220000_001.jpg".
Ok, here's my take when you want the number with max two zeroes padding. $num has to be an integer for the .tostring() method I want.
dir *.jpg | rename-item -newname { $a,$b,$c,$num = $_.basename -split '_'
$num = [int]$num
$a + '_' + $b + "_" + $c + '_' + $num.tostring('000') + '.jpg'
} -whatif
the following presumes your last part of the .BaseName will always need two zeros added to it. what it does ...
fakes getting the fileinfo object that you get from Get-Item/Get-ChildItem
replace that with the appropriate cmdlet. [grin]
splits the .BaseName into parts using the _ as the split target
adds two zeros to the final part from the above split
merges the parts into a $NewBaseName
gets the .FullName and replaces the original BaseName with the $newBaseName
displays that new file name
you will still need to do your rename, but that is pretty direct. [grin]
here's the code ...
# fake getting a file info object
# in real life, use Get-Item or Get-ChildItem
$FileInfo = [System.IO.FileInfo]'PRT14_WD_14220000_1.jpg'
$BNParts = $FileInfo.BaseName.Split('_')
$BNParts[-1] = '00{0}' -f $BNParts[-1]
$NewBasename = $BNParts -join '_'
$NewFileName = $FileInfo.FullName.Replace($FileInfo.BaseName, $NewBaseName)
$NewFileName
output = D:\Data\Scripts\PRT14_WD_14220000_001.jpg
The -replace operator operates on regexes (regular expressions), not wildcard expressons such as * (by itself), which is what you're trying to use.
A conceptually more direct approach is to focus the replacement on the end of the string:
Get-ChildItem | # `dir` is a built-in alias for Get-ChildItem`
Rename-Item -NewName { $_.Name -replace '(?<=_)[^_]+(?=\.)', '00$&' } -WhatIf
Note: The -WhatIf common parameter in the command above previews the operation. Remove -WhatIf once you're sure the operation will do what you want.
(?<=_)[^_]+(?=\.) matches a nonempty run (+) of non-_ chars. ([^_]) preceded by _ ((?<=_) and followed by a literal . ((?=\.)), excluding both the preceding _ and the following . from what is captured by the match ((?<=...) and (?=...) are non-capturing look-around assertions).
In short: This matches and captures the characters after the last _ and before the start of the filename extension.
00$& replaces what was matched with 00, followed by what the match captured ($&).
In a follow-up comment you mention wanting to not just blindly insert 00, but to 0-left-pad the number after the last _ to 3 digits, whatever the number may be.
In PowerShell [Core] 6.1+, this can be achieved as follows:
Get-ChildItem |
Rename-Item -NewName {
$_.Name -replace '(?<=_)[^_]+(?=\.)', { $_.Value.PadLeft(3, '0') }
} -WhatIf
The script block ({ ... }) as the replacement operand receives each match as a Match instance stored in automatic variable $_, whose .Value property contains the captured text.
Calling .PadLeft(3, '0') on that captured text 0-left-pads it to 3 digits and outputs the result, which replaces the regex match at hand.
A quick demonstration:
PS> 'PRT14_WD_14220000_1.jpg' -replace '(?<=_)[^_]+(?=\.)', { $_.Value.PadLeft(3, '0') }
PRT14_WD_14220000_001.jpg # Note how '_1.jpg' was replaced with '_001.jpg'
In earlier PowerShell versions, you must make direct use of the .NET [regex] type's .Replace() method, which fundamentally works the same:
Get-ChildItem |
Rename-Item -NewName {
[regex]::Replace($_.Name, '(?<=_)[^_]+(?=\.)', { param($m) $m.Value.PadLeft(3, '0') })
} -WhatIf

How to rename files with sequential even and odd numbers in PowerShell?

I would like to know how I can rename the files from a specific folder with a sequence of only even and odds numbers in PowerShell. E.g. Folder1: pag_001.jpg, pag_003.jpg, pag_005.jpg.... pag_201.jpg , Folder2: pag_002.jpg, pag_004.jpg, pag_006.jpg.... pag_200.jpg. It is because I have a document that was scanned first the odds pages and secondly the even pages, therefore their file names are in a consecutive sequence from 1 to 201. Then I separated one half of the files which are the odds pages in a new place: Folder1, and the second half,the even pages in the Folder2. That is why I would like change the names first and the join again together with their new names.
I have tried this based in a similar post:
At the moment I could generate even number sequences like that:
ForEach ($number in 1..100 ) { $number * 2}
and odd numbers like that:
ForEach ($number in 0..100 ) { $number *2+1}
and wanted apply the sequences generated before to rename my files like that:
cd C:\test\Folder1
$i = $number * 2
Get-ChildItem *.jpg | %{Rename-Item $_ -NewName ('pag_{0:D3}.jpg' -f $i++)}
but it doesn't works! Any suggestions are welcome
Regards,
Your $i++ adds 1 each time, this is why it also add even numbers,
You can create array of Odd Numbers then use the $i++ to step one item in the array, like this:
$path = "C:\test\Folder1"
$oddNumbersArray = 0..100 | % {$_ *2 +1}
$i = 0
Get-ChildItem $path -Filter *.jpg | % {Rename-Item $_ -NewName ("pag_$($oddNumbersArray[$i]).jpg") ;$i++}
For Even Numbers change the $oddNumbersArray line to {$_ *2}
Bunch of ways to do this. For mine we add each index as a member so that it is more easily accessible in the rename item script block.
$index = 0
Get-ChildItem $path -Filter "*.jpg" | ForEach-Object{
$index = $index +2
$_ | Add-Member -Name "Index" -MemberType NoteProperty -Value $index -PassThru
} | Rename-Item -NewName {'pag_{0:D3}.jpg' -f $_.Index} -WhatIf
Using Add-Member in a ForEach-Object we update the value of index and then add it as a property of the same name. Then in your rename-item scriptblock we can call that property. Remove the -WhatIf after you verified the new names are what you wanted. Switch $index between 0 and -1 for even and odd respectively.
Another method using a global index variable and mitigating the pipeline by using calculated properties to create the pipeline variables that Rename-Item uses.
$path = "C:\Temp\csv"
$global:index = 0 # Use -1 for odd
Get-ChildItem $path -Filter "*.csv" |
Select-Object #{Name="Path";Expression={$_.FullName}},
#{Name="NewName";Expression={$global:index = $global:index + 2; 'pag_{0:D3}.jpg' -f $global:index}} |
Rename-Item -WhatIf