Bulk renaming of files in PowerShell with sequential numeric suffixes - powershell

I have 14,000 pictures sorted into files by year and month but taken with multiple cameras and I want the file name to reflect the date taken. For example October 16, 1998 pictures are in a folder called 1998\10 October\19981016.
I want the all the pictures to be named 19981016_0001 19981016_0002 etc. I tried multiple suggestions but it hasn't worked.
I can get to the point where it lists the folder I want to change but I'm unable to actually change it. All of my pictures are .jpg.
Attempts
I created a temp file of copies in case I messed it up. I started by typing cd "C:\Documents and Settings\Brooke LastName\Desktop\Temp" then after successfully getting my file to load I used a formula I found on this forum.
ls *jpg | Foreach {$i=1} {Rename-Item _ -NewName ("$($.19981016){0:00000000#} .jpg" -f $i++) -whatif}
The error I got said
Unexpected token ' .19981016' in expression or statement.
At line:1 char:12 + $.19981016 <<<<
The error repeated several times.
I found several formulas on the web but most created files that would number with parentheses for example vacation (1).jpg. I want a four digit counter after an underscore at the end of my date, e.g. 19981016_0001

The syntax is way off. A few issues:
I'm not sure what $($.19981016) was intended to produce, but $( ) evaluates the expression in the parentheses and interpolates the result into the string, and you're getting the error because $.19981016is not a valid expression. The error would be repeated for each .jpg file in the directory.
{0:00000000#} in a formatted string will create a zero-padded number of 9 digits, but a shorter way to do that is {0:D9}. However, I thought you wanted the zero-padded number to have 4 digits, so that should be {0:0000#} or {0:D4}.
I'm not sure what Foreach {$i=1} { [...] is supposed to do. The keyword foreach can mean a foreach loop, or it can be shorthand for Foreach-Object. In this context, receiving input from a pipeline, it's necessarily the latter, but this syntax is incorrect either way.
This will do what you want, if I understand the description correctly:
$i = 1
Get-ChildItem *.jpg | %{Rename-Item $_ -NewName ('19981016_{0:D4}.jpg' -f $i++)}
The filenames will be 19981016_0001.jpg, 19981016_0002.jpg, 19981016_0003.jpg, etc.
A few notes:
You said that you want filenames like 19981016_0001, but I'm assuming you want to keep the .jpg extension.
You need to initialize $i, otherwise it will start from 0 if it's not yet defined, or worse yet, from some other number if $i was used previously in the same PowerShell session. For example, if you have 1,432 .jpg files in the directory, and you run the command first with -WhatIf, and then run it for real, the numbers will start from 1432.
$i++ increments $i by 1. However, if you're using it as a value, it increments after the value is read; that's why if $i is undefined, it will start from 0.
Get-ChildItem is the same as ls. I used the native PowerShell name, but they're interchangeable (ls, dir, and gci are all aliases for Get-ChildItem).
%{ } is shorthand for Foreach-Object

This comes close to the original question. You can actually pass a script block array to foreach-object -process, as documented (barely). I only know this after Bruce Payette's book. The containing folder name is "19981016". The source filenames may not go in the order you expect.
ls *jpg |
Foreach {$i=1} {Rename-Item $_ -NewName ("$($_.directory.name)_{0:000#}.jpg" -f
$i++) -whatif}
What if: Performing the operation "Rename File" on target "Item: C:\users\js\19981016\file1.jpg Destination: C:\users\js\19981016\19981016_0001.jpg".
What if: Performing the operation "Rename File" on target "Item: C:\users\js\19981016\file10.jpg Destination: C:\users\js\19981016\19981016_0002.jpg".
What if: Performing the operation "Rename File" on target "Item: C:\users\js\19981016\file2.jpg Destination: C:\users\js\19981016\19981016_0003.jpg".

$path="C:\..."
$files=Get-ChildItem $path -Filter *.gif
Foreach($obj in $files){
$pathWithFilename=$path + $obj.Name;
$newFilename= "file_"+$obj.Name;
Rename-Item -Path $pathWithFilename -NewName $name
}
You can use this code blocks in powershell ISE. Example output like this :
Old files :
1232.gif
asd.gif
After run code :
file_1232.gif
file_asd.gif

Related

How to rename folders to put the year first using Powershell

I'm trying to organize some old photos that are split into many different folders. All of the folder names do contain the year, but almost always at the end of the folder name. This doesn't work very well when I'm trying to sort all of my photos from the past 20 years. I'm trying to write a script that would loop through all of the folder names and move the year (YYYY) to the beginning of the folder name.
Current Folders:
The best trip ever 2012
Visiting relatives 2010
2017 trip
Old photos from 2001
Convert to:
2012 The best trip ever
2010 Visiting relatives
2017 trip
2001 Old photos from
I am not very familiar with powershell so I've spent a few hours fiddling with regex and the necessary scripts to filter to the right subset of folder names (that start with a letter and contain a 4 digit year) but I'm struggling to actually rename these successfully.
Here's what I have:
$folders = Get-ChildItem -Path C:\Users\User\pictures\ | Where-Object { $_.Name -match '^[a-zA-Z].+[0-9]{4}' }
foreach ($folder in $folders)
{ $folder.Name.Split() | Where {$_ -match "[0-9]{4}"}
Rename-Item -Path $folder-NewName "$($Matches[0])_$folder.Name"
}
Any help is appreciated!
If you use the -match operator with a regex that captures the name parts of interest via capture groups ((...)), you can rearrange these name parts, as reflected in the automatic $Matches variable variable, in a delay-bind script block passed to the Rename-Item call:
Get-ChildItem -Directory C:\Users\User\pictures |
Where-Object Name -match '^(.+?) ?\b(\d{4})\b(.*?)$' |
Rename-Item -WhatIf -NewName {
'{0} {1}{2}' -f $Matches.2, $Matches.1, $Matches.3
}
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.
For an explanation of the regex and the ability to interact with it, see this regex101.com page.
Note: The following simplification, which uses the -replace operator, works in principle, but, unfortunately, reports spurious Source and destination path must be different errors as of PowerShell 7.2.1, for all directories whose name either doesn't contain a 4-digit year or already has it at the start of the name:
# !! Works, but reports spurious errors as of PowerShell 7.2.1
Get-ChildItem -Directory C:\Users\User\pictures
Rename-Item -WhatIf -NewName { $_.Name -replace '^(.+?) ?\b(\d{4})\b(.*?)$' }
The reason is Rename-Item's unfortunate behavior of complaining when trying to rename a directory to its existing name (which happens when the -replace operation doesn't find a regex match and therefore returns the input string as-is), which should be a quiet no-op, as it already is for files - see GitHub issue #14903.

Extract words from filename delineated by underscores and spaces in Powershell

I am trying to extract two words from filenames. The names have the format:
__XXXXXXXX_XXX_XXXXXXX_XXXX_XXXXX_XXXX XXX_Aircraft 017_XXXXXXXX-XXXXXXX_XXXXXXX-XXXXXXX-XXXXXX-01Apr2021-XXXXX
With the X's being replaced with different words. I need to extract the aircraft number and the date so that I can rename the files with just that information. Using help from this site I have tried the following to isolate the aircraft number:
$names = gci -Path "H:\Path\to\Logs" *.log -Recurse | select #{n="Name"; e={if ($_.Name -match "Aircraft (\w+)") {
$matches[1] }}}
However, it doesn't seem to give me the match I need. However, I am very inexpert in programming and may be going down the wrong path. My hope is that the same logic used to isolate the aircraft number also applies for the date.
# Create a sample file.
$file = New-Item '__XXXXXXXX_XXX_XXXXXXX_XXXX_XXXXX_XXXX XXX_Aircraft 017_XXXXXXXX-XXXXXXX_XXXXXXX-XXXXXXX-XXXXXX-01Apr2021-XXXXX'
# Substitute your `Get-ChildItem` command for $file
$file |
Rename-Item -WhatIf -NewName {
if ($_.Name -match '_(Aircraft \w+?)_.+(\d{2}[a-z]{3}\d{4})-') {
# Synthesize the new file name from the extracted substrings.
'{0} - {1}' -f $Matches[1], $Matches[2]
} else {
# Input file name didn't match, (effectively) do nothing.
$_.Name
}
}
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.
For an explanation of the regex used with the -match operator above, see this regex101.com page.[1]
The above uses two capture groups ((...)) to capture the substrings of interest, which can be accessed via indices 1 and 2 of the automatic $Matches variable.
-f, the format operator is then used to build the output file name from the captured substrings. Tweak the LHS format string as needed.
Thanks to -WhatIf, you'll see output such as the following, which is the preview of what would happen when you remove -WhatIf - note the new file name in the Destination: path:
What if: Performing the operation "Rename File" on target
"Item: /tmp/__XXXXXXXX_XXX_XXXXXXX_XXXX_XXXXX_XXXX XXX_Aircraft 017_XXXXXXXX-XXXXXXX_XXXXXXX-XXXXXXX-XXXXXX-01Apr2021-XXXXX
Destination: /tmp/Aircraft 017 - 01Apr2021".
Note how a script block ({ ... }) is passed as an argument to Rename-Item's -NewName parameter, which then acts on each input file via the automatic automatic $_ variable and outputs the argument value to use for the input object at hand. Such script blocks are called delay-bind script blocks.
[1] Note that even though regex101.com, a site for visualizing, explaining and experimenting with regexes, doesn't support the .NET regex engine used by PowerShell, choosing a similar engine, such as Java's, usually exhibits the same behavior, at least fundamentally.

Using Powershell to reverse the sequence numbers in a set of file names

I am new to using PowerShell (and coding for that matter), all I'm trying to do is rename my files).
I have 222 .jpg's that are named
"REF_10001.jpg"
"REF_10002.jpg"
"REF_10003.jpg"
etc
but my problem is that they are in the wrong order, I need to reverse them, for example I need
"REF_10001.jpg" --> "REF_10222.jpg"
and vice versa.
"REF_10222.jpg" --> "REF_10001.jpg"
Is there a way to do this? I have been struggling for hours to rename my files properly, just to realize they are in the wrong order, I am tempted to just go do them individually at this point.
Also if someone could help me to change the files to read
"REF.0001.jpg"
"REF.0002.jpg"
etc.
that would be great.
Or if what I'm asking isn't possible, I have a back up folder of my image sequence before i started trying to rename them, the files read
_0221_Layer 1.jpg
_0220_Layer 2.jpg
...
_0000_Layer 222.jpg
I need to change them so that
"_0221_Layer 1.jpg" --> "Ref.0001.jpg"
...
"_0000_Layer 222.jpg" --> "Ref.0222.jpg"
Try this:
Get-ChildItem ".\Pics\*.jpg" |
ForEach-Object {$i = 1} {
Rename-Item -LiteralPath $_.FullName -NewName "$($_.BaseName)-$i.jpg"
$i++
}
Get-ChildItem ".\Pics\*.jpg" |
ForEach-Object {$j = 222} {
Rename-Item -LiteralPath $_.FullName -NewName "REF_$("1{0:0000}" -f $j).jpg"
$j--
}
It's seems a bit inefficient to me, in that you need to enumerate twice, but it works on my test files, so will hopefully be good enough to resolve your immediate problem.
The first loop adds a unique 'tag' to the filename that is later discarded - this is needed otherwise you end up with clashes (e.g. if you try to name "REF_10001" to "REF_10222", it will fail since the latter already exists.
My assumptions:
We do not know in advance if the numbers in the file names always follow each other
We do not know in advance the number of digits in these numbers
I would only take files starting REF_ followed by numbers and ending with the extension jpg
So I propose a solution that reverses the first name with the last one, the second with the last before and so on :
$DirWithFiles="C:\Temp\DirWithFilesToRename"
#Creation to new dir for copy
$Temporatydir= [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName())
New-Item -ItemType Directory $Temporatydir
#get all file jgp with format REF_ + Number + .jpg and order by number
$ListFile=Get-ChildItem $DirWithFiles -File -Filter "REF_*.jpg" | where BaseName -Match "^REF_\d{1,}$" | select Fullname, Name, #{N="Number";E={[int]($_.BaseName -split '_')[1]}} | sort Number
#copy files into new temporary dir and rename 1 position by N position, 2 by N-1, 3 by N - 2 etc...
for ($i = 0; $i -lt ($ListFile.Count / 2 ); $i++)
{
Copy-Item $ListFile[$i].FullName "$Temporatydir\$($ListFile[$ListFile.Count-$i - 1].Name)" -Force
Copy-Item $ListFile[$ListFile.Count-$i - 1].FullName "$Temporatydir\$($ListFile[$i].Name)" -Force
}
#Copy all files in temporary dir to initial dir with overwriting
Get-ChildItem $Temporatydir | Copy-Item -Destination $DirWithFiles
#delete temporary dir
Remove-Item $Temporatydir -Recurse
A simple method to reverse the order is:
subtract the highest number+1 from each file number.
to remove the resulting negative sign multiply with -1 or use the [math]::abs() function.
To get the number from the (Base)Name a Regular Expression '^REF_1(\d+)?' captures () the number without the leading 1.
a format string operator "REF.{0:0000}.jpg" -f is used to create the name inserting the calculated new number with 4 places
As the prefix changes there is danger overwriting a file from the other end.
Get-ChildItem REF_1*.jpg |
Where-Object BaseName -match '^REF_1(\d+)?' |
Rename-Item -NewName {"REF.{0:0000}.jpg" -f (-1 *($Matches[1]-223))} -whatif
If the output looks OK remove the trailing -WhatIf parameter.
Sample output last line:
What if: Performing the operation "Rename File" on target "Item: REF_10222.jpg
Destination: REF.0001.jpg".
The following works with a variable number of files and also performs the desired name transformation (in addition to reversing the sequence numbers):
# Enumerate the files in reverse alphabetical order - which thanks to the 0-padding
# is equivalent to the reverse sequence-number order - and rename them with
# an ascending 0-padded sequence number.
# Note that in-place renaming is only possible because the new filename pattern
# - REF.*.jpg - doesn't conflict with the old one, REF_1*.jpg
$iref = [ref] 0 # sequence-number helper variable
Get-ChildItem -Filter REF_1*.jpg | Sort-Object Name -Descending |
Rename-Item -NewName { 'REF.{0:0000}.jpg' -f (++$iref.Value) } -WhatIf
Note: The use of -WhatIf previews the command without actually running it.
If the preview shows the intended operations, remove -WhatIf to actually perform it.
Note the need to use a [ref]-typed aux. variable for the sequence number and to increment it via its .Value property in the script block ({ ... }) that calculates the new name, because the script block runs in a different variable scope and therefore cannot directly modify variables in the caller's scope.

Powershell, rename-item doesn't work as expected

I have a bunch of jpg image files named in the following pattern:
0001-rand01_012.jpg
0002-rand03_034.jpg
I want to rename them by removing the first 5 characters to get the form:
rand01_012.jpg
etc..
I use the following command:
Get-ChildItem | Rename-Item -NewName {$_.name.Substring(5)}
When using this with -whatif flag i get the expected message saying:
Performing the operation "Rename File" on target "Item: C:\Users\xxxx\Documents\xx xx\temp2\0123-rand16_030.jpg Destination: C:\Users\
xxxx\Documents\xx xx\temp2\rand16_030.jpg".
But removing the whatif gives me errors of this type:
Rename-Item : The input to the script block for parameter 'NewName' failed. Exception calling "Substring" with "1" argument(s): "startIndex cannot be
larger than length of string.
followed by a whole bunch of:
Rename-Item : Cannot create a file when that file already exists.
The files themselves are renamed with random number of characters removed rather than 5 as was intended. So they have ended up like:
01.jpg
01.jpg
.
.
.
d14_001.jpg
etc.
I have used this command to rename such files in the past with success. The fact that I'm getting such random results is making me pull my hair out.
tl;dr
Make sure you only process the files of interest:
(Get-ChildItem -File [0-9][0-9][0-9][0-9]-*.jpg) |
Rename-Item -NewName {$_.name.Substring(5)} -WhatIf
The -WhatIf common parameter in the command above previews the operation. Remove -WhatIf once you're sure the operation will do what you want.
In PowerShell [Core] 6+, placing (...) around Get-ChildItem is no longer technically necessary, but advisable.[1]
That way:
You rule out unrelated files up front.
Even if something goes wrong, you can correct the problem and run the command again to reprocess only the failed files, without affecting the previously renamed files.
The most likely reason for something going wrong is more than 1 input file resulting in the same filename after removing the 5 first char.
It sounds like you've mistakenly run the command repeatedly, so you've cut off 5 chars. multiple times:
0001-rand01_01.jpg -> rand01_01.jpg -> _01.jpg
Once a filename has fewer than 5 chars., you'll get the the startIndex-related error, because the [string] class's .Substring() method doesn't accept an index beyond the length of the string (try 'ab'.Substring(3)).
That said, since you're running Get-ChildItem without a filter and therefore return all (non-hidden) child items, you may be processing unrelated files ore even directories whose names are too short.
The Cannot create a file when that file already exists. errors are just follow-on errors that result from the script block that normally returns the new name effectively returning the empty string, so Rename-Item is somewhat obscurely complaining that you can't rename a file to its current name.
That said, you can even get Cannot create a file when that file already exists errors during the first run, namely if more than 1 input file with its first 5 chars. chopped off results in the same filename.
E.g., 0001-rand01_012.jpg and 0002-rand01_012.jpg would both be renamed to rand01_012.jpg, which fails once the first one has been renamed.
That is, for your command to work as intended, all filenames that result from dropping the first 5 chars. must be unique.
Here's an MCVE (Minimal, Complete, and Verifiable Example):
Setup:
# Create and change to temp dir.
Set-Location (mkdir temp)
# Create sample input files named 0001-rand01_01.jpg, 0002-rand01_02.jpg, ...
# Note how the suffixes after the first 5 char. must be *unique*.
1..9 | %{ $null > "000${_}-rand01_0${_}.jpg" }
1st run:
# No errors
> (Get-ChildItem -File) | Rename-Item -NewName { $_.Name.Substring(5) }
# Show new names
> Get-ChildItem | Select Name
Name
----
rand01_01.jpg
rand01_02.jpg
rand01_03.jpg
rand01_04.jpg
rand01_05.jpg
rand01_06.jpg
rand01_07.jpg
rand01_08.jpg
rand01_09.jpg
A 2nd run yields:
Name
----
1_01.jpg
1_02.jpg
1_03.jpg
1_04.jpg
1_05.jpg
1_06.jpg
1_07.jpg
1_08.jpg
1_09.jpg
At the time of the 3rd run, all the names are too short, and all you'll get is Rename-Item : Cannot create a file when that file already exists. errors.
[1] Enclosing Get-ChildItem in (...) ensures that the matching files are collected in an array, up front, before Rename-Item is invoked.
This explicitly prevents already-renamed files from getting re-enumerated by Get-ChildItem and thus interfering with the iteration. Explicit use of (...) is technically no longer necessary in PowerShell [Core] 6+ (it is necessary in Windows PowerShell (5.1-)), because Get-ChildItem is implemented in a way that always internally collects info about all files up front, across platforms, because it sorts them by name, which is inherently only possible after all names have been collected.
In light of that, whether you use (...) or not should functionally amount to the same, although using (...) is advisable, because it doesn't rely on what amounts to an implementation detail (the documentation doesn't mention how the outputs are ordered).

Renaming files in bulk and in ascending order in CMD

I know this question was already asked by someone but I will ask again.
Can someone tell me how to rename in bulk and in ascending order if possible in CMD. I already tried renaming in powershell but to no avail. It only let me use once and I need to rename another folder files but to no avail. It didn't let it rename files in another folder. This is the code I use in powershell:
$i = 1
Get-ChildItem *.mkv | %{Rename-Item $_ -NewName ('Haikyuu - {0:D2}.mkv' -f $i++)}
I'm renaming my anime series per folder and some of my copies have 100+ videos. and somehow you could teach me what each code mean (the code that must use in CMD). The ones I've searched can't understand it in layman's term or doesn't tell the user how it's supposed to work. Thank you in advance. by the way, the folder is placed in an external drive.
so from the beginning:
$i= variable for storing the initial value 1
Get-ChildItem = is like "dir" which lists the files and folder under a certain path.
In this case, it is listing all the files which starts with anything but have the extension .mkv
* indicates wildcard.
| = pipeline which passes the output of the first command as an input of the next command.
% = ForEach-Object is iterating each object one by one coming from the pipeline.
$_= Current pipeline object . Here it is taking each object one by one and renaming it using Rename-Item
-NewName = is the parameter of the Rename-Item which asks for the new name to pass.
Hope it clarifies your need.
The reason why I can't rename my video files is there were [brackets] on the filename.
So I use this:
Get-ChildItem -Recurse -Include *.mkv | Rename-Item -NewName { $_.Name.replace("[","").replace("]","").replace("(","").replace(")","") }
Which on the same directories, I can access subfolders too to omit brackets and parethesis. then I proceed using the code above in the question to rename my files in every folder. The Reason why I'm doing the 'renaming' per folder is that, each folder is different anime series. but the code above is working.
if anyone can give me less code than repeating the 'replace' and concatenating it, I will gladly accept and choose that as the best answer. :)
If you use the parameter -LiteralPath for the source, no prior renaming is necessary.
%i = 1
Get-ChildItem *.mkv |
ForEach {Rename-Item -LiteralPath "$_" -NewName ('Haikyuu - {0:D2}.mkv' -f $i++)}
A hint on sorting, I hope the present numbering of the source files has a constant width, otherwise the result is mixed up as an alphabetic sort (which is inherent to ntfs formatted drives) will sort the number 10 in front of 2.
To check this append the parameter -whatif to the Rename-Item command