As I wrote this article, I realized it's really as much about string manipulation as renaming files per se.
The code and examples in this article work with PowerShell version 3 and up (Windows Management Framework 3.0), unless otherwise stated. Some of the things will have to be done differently with PowerShell version 2 and earlier, but the Rename-Item and code in the -NewName parameter itself is compatible with v2.I will be using Get-ChildItem and one of its aliases: "dir" (also "ls" and "gci"). They are the same (dir, ls and gci are aliases to Get-ChildItem).
Remember that you can always add the flag "-Recursive" to Get-ChildItem to replace recursively.If you are using Get-ChildItem in PSv2, you can filter out only files using Where-Object and the boolean property "PSIsContainer". In PowerShell v3 and up you have the -File and -Directory parameters for Get-ChildItem for filtering this.
For files only in PSv2:Get-ChildItem -Path C:\temp | Where { -not $_.PSIsContainer } | Rename-Item ...For directories only in PSv2:
Get-ChildItem -Path c:\temp | Where { $_.PSIsContainer } | Rename-Item ...
PS C:\temp\dir> "" > file.txt # creates an empty file called file.txt PS C:\temp\dir> Rename-Item -Path file.txt -NewName NewFileName.txt PS C:\temp\dir> (dir .\NewFileName.txt).Name NewFileName.txt
I should mention the -WhatIf parameter right off the bat. If you're unsure, use test files, and if you trust the PowerShell team to always implement -WhatIf support in the Rename-Item cmdlet, you can run it against the real deal as well. Testing the actual -WhatIf first is something we paranoid people do on new systems.
PS C:\temp\dir> dir -Path test*.log | Rename-Item -NewName { $_ -replace '\.log$', '.txt' } -WhatIf What if: Performing the operation "Rename File" on target "Item: C:\temp\dir\test.log Destination: C:\temp\dir\test.txt".
Uppercasing the entire file name can be done, for instance like this:
PS C:\temp\dir> (Get-ChildItem -File).Name random-01.txt random-02.txt random-03.txt random-04.txtPS C:\temp\dir> Get-ChildItem -File -Path *.txt | Rename-Item -NewName { $_.Name.ToUpper() }
PS C:\temp\dir> (Get-ChildItem -File).Name RANDOM-01.TXT RANDOM-02.TXT RANDOM-03.TXT RANDOM-04.TXT
To make the same file names all lowercase, we can use the same technique, except for replacing the System.String method ToUpper() (here used on the string "$_.Name") with ToLower().
PS C:\temp\dir> (dir -File).Name RANDOM-01.TXT RANDOM-02.TXT RANDOM-03.TXT RANDOM-04.TXTPS C:\temp\dir> Get-ChildItem -File -Path *.txt | Rename-Item -NewName { $_.Name.ToLower() }
PS C:\temp\dir> (dir -File).Name random-01.txt random-02.txt random-03.txt random-04.txt
PS C:\temp\dir> (dir -File).Name pseudorandom-01.txt pseudorandom-02.txt pseudorandom-03.txt random-06.txt random-07.txt random-08.txt PS C:\temp\dir> dir -File | Rename-Item -NewName { $_.BaseName + '.log' } PS C:\temp\dir> (dir -File).Name pseudorandom-01.log pseudorandom-02.log pseudorandom-03.log random-06.log random-07.log random-08.log
The above approach would generally be preferred. Using a regex, you could do something like the following, where I change the extensions back to .txt.
PS C:\temp\dir> dir -File | Rename-Item -NewName { $_.Name -replace '(\.[^.]+)$', '.txt' } PS C:\temp\dir> (dir -File).Name pseudorandom-01.txt pseudorandom-02.txt pseudorandom-03.txt random-06.txt random-07.txt random-08.txt
Again, we're using the script block argument for -NewName, and this is a breeze. See demo code. The -replace operator, which you can read more about here, takes a regex (regular expression) as its first parameter, and a string as its second (if the second parameter is left out, the regex part will be removed, as if you had passed an empty string).
The regex meta-character "^" means "the beginning of the string". "$" means the end (or before a newline before the end - read more about PowerShell regular expressions here).PS C:\temp\dir> Get-ChildItem -Path *.txt | Format-Table -AutoSize Directory: C:\temp\dir Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2016-08-15 21:41 1024 random-01.txt -a--- 2016-08-15 21:41 1024 random-02.txt -a--- 2016-08-15 21:41 1024 random-03.txt -a--- 2016-08-15 21:41 1024 random-04.txtPS C:\temp\dir> Get-ChildItem -Path *.txt | Rename-Item -NewName { $_.Name -replace '^random-', 'random-x-' }
PS C:\temp\dir> Get-ChildItem -Path *.txt | Format-Table -AutoSize Directory: C:\temp\dir Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2016-08-15 21:41 1024 random-x-01.txt -a--- 2016-08-15 21:41 1024 random-x-02.txt -a--- 2016-08-15 21:41 1024 random-x-03.txt -a--- 2016-08-15 21:41 1024 random-x-04.txt
Another, equally complex, example (it gets a bit worse if you read on). Let's pretend we have a need to add the string "y" after every letter (string) "x" in a file name, but only if this letter x is surrounded by hyphens on either side.
Using the same files as in the example above, we'll do just this, using the -replace operator again.PS C:\temp\dir> Get-ChildItem -Path *.txt | Rename-Item -NewName { $_.Name -replace '-x-', '-xy-' } PS C:\temp\dir> Get-ChildItem -Path *.txt | Format-Table -AutoSize Directory: C:\temp\dir Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2016-08-15 21:41 1024 random-xy-01.txt -a--- 2016-08-15 21:41 1024 random-xy-02.txt -a--- 2016-08-15 21:41 1024 random-xy-03.txt -a--- 2016-08-15 21:41 1024 random-xy-04.txt
Now we'll increase the complexity a bit and pretend our logical requirement is to insert a string before the first number that might occur in a file name (we'll do before the last number next). Let's go for the string "zzz", to be sufficiently boring.
To do this, I'll use Get-ChildItem, a pipe, and a script block for Rename-Item's -NewName parameter again, and the -replace operator with captures.PS C:\temp\dir> Get-ChildItem -Path *.txt | Format-Table -AutoSize Directory: C:\temp\dir Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2016-08-17 21:58 1024 random-01.txt -a--- 2016-08-17 21:58 1024 random-02.txt -a--- 2016-08-17 21:58 1024 random-03.txt -a--- 2016-08-17 21:58 1024 random-04.txtPS C:\temp\dir> Get-ChildItem -Path *.txt | Rename-Item -NewName { $_.Name -replace '^(\D+)(\d.*)', '${1}zzz${2}' }
PS C:\temp\dir> Get-ChildItem -Path *.txt | Format-Table -AutoSize Directory: C:\temp\dir Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2016-08-17 21:58 1024 random-zzz01.txt -a--- 2016-08-17 21:58 1024 random-zzz02.txt -a--- 2016-08-17 21:58 1024 random-zzz03.txt -a--- 2016-08-17 21:58 1024 random-zzz04.txt
As promised, here's how to insert the string "x" before the last number in the file name (string).
PS C:\temp\dir> Get-ChildItem -Path *.txt | Format-Table -AutoSize Directory: C:\temp\dir Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2016-08-17 22:21 1024 random-01.txt -a--- 2016-08-17 22:21 1024 random-02.txt -a--- 2016-08-17 22:21 1024 random-03.txt -a--- 2016-08-17 22:21 1024 random-04.txtPS C:\temp\dir> Get-ChildItem -Path *.txt | Rename-Item -NewName { $_.Name -replace '^(.*)(\d.*?)$', '${1}x${2}' }
PS C:\temp\dir> Get-ChildItem -Path *.txt | Format-Table -AutoSize Directory: C:\temp\dir Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2016-08-17 22:21 1024 random-0x1.txt -a--- 2016-08-17 22:21 1024 random-0x2.txt -a--- 2016-08-17 22:21 1024 random-0x3.txt -a--- 2016-08-17 22:21 1024 random-0x4.txt
PS C:\temp\dir> Get-ChildItem -Path *.txt | Format-Table -AutoSize Directory: C:\temp\dir Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2016-08-17 22:21 1024 random-0x1.txt -a--- 2016-08-17 22:21 1024 random-0x2.txt -a--- 2016-08-17 22:21 1024 random-0x3.txt -a--- 2016-08-17 22:21 1024 random-0x4.txtPS C:\temp\dir> Get-ChildItem -Path *.txt | Rename-Item -NewName { $_.Name -replace '^(.{3}).{3}(.*)', '${1}${2}' }
PS C:\temp\dir> Get-ChildItem -Path *.txt | Format-Table -AutoSize Directory: C:\temp\dir Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2016-08-17 22:21 1024 ran-0x1.txt -a--- 2016-08-17 22:21 1024 ran-0x2.txt -a--- 2016-08-17 22:21 1024 ran-0x3.txt -a--- 2016-08-17 22:21 1024 ran-0x4.txt
I'll use the [regex] class' Replace() method with a script block as the third argument. This is a "match evaluator", and again I refer to the regex article for further information.
PS C:\temp\dir> Get-ChildItem -Path *.txt | Format-Table -AutoSize Directory: C:\temp\dir Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2016-08-17 22:21 1024 ran-0x1.txt -a--- 2016-08-17 22:21 1024 ran-0x2.txt -a--- 2016-08-17 22:21 1024 ran-0x3.txt -a--- 2016-08-17 22:21 1024 ran-0x4.txtPS C:\temp\dir> Get-ChildItem -Path *.txt | Rename-Item -NewName { [regex]::Replace($_.Name, '(\d+)', { $TempNum = [int] $args[0].Groups[1].Value; $TempNum + 1 }) }
PS C:\temp\dir> dir -File Directory: C:\temp\dir Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2016-08-17 22:21 1024 ran-1x2.txt -a--- 2016-08-17 22:21 1024 ran-1x3.txt -a--- 2016-08-17 22:21 1024 ran-1x4.txt -a--- 2016-08-17 22:21 1024 ran-1x5.txt
Now, if you're going to increment numbers of log files that have names as shown here, and also want to preserve the zero-padding, but still perform numerical operations, you will soon realize that Rename-Item will clash with existing files, except for the last one, as demonstrated here:
PS C:\temp\dir> (dir -File).Name random_file-01.log random_file-02.log random_file-03.log random_file-04.log random_file-05.log random_file-06.log random_file-07.log random_file-08.log random_file-09.log random_file-10.log random_file-11.log PS C:\temp\dir> dir -File | Rename-Item -NewName { [regex]::Replace($_.Name, '(\d+)', { $n = [int] $args[0].Groups[1].Value $n += 1 "{0:D2}" -f $n }) } Rename-Item : Cannot create a file when that file already exists. At line:1 char:13 + dir -File | Rename-Item -NewName { + CategoryInfo : WriteError: (C:\temp\dir\random_file-01.log:String) [Rename-Item], IOException + FullyQualifiedErrorId : RenameItemIOError,Microsoft.PowerShell.Commands.RenameItemCommand Rename-Item : Cannot create a file when that file already exists. At line:1 char:13 + dir -File | Rename-Item -NewName { + CategoryInfo : WriteError: (C:\temp\dir\random_file-02.log:String) [Rename-Item], IOException + FullyQualifiedErrorId : RenameItemIOError,Microsoft.PowerShell.Commands.RenameItemCommand .... # and 8 more of these ...
And we can see that only the last file was actually renamed:
PS C:\temp\dir> (dir -File).Name random_file-01.log random_file-02.log random_file-03.log random_file-04.log random_file-05.log random_file-06.log random_file-07.log random_file-08.log random_file-09.log random_file-10.log random_file-12.log
Notice how only the last file name changed. It's a good thing Rename-Item didn't just overwrite. I'm guessing the -Force parameter will make it do that (you usually really don't want that).
One trick to pull out that will often work is simply sorting on the name descendingly, but beware that if the numbers aren't zero-padded (e.g. "001" rather than "1"), the sorting will be as text, and may give you inconsistent and bad results. You could pull out the tricks I mention in the article I wrote about sorting with numbers zero-padded in the sorting for those situations.Here's the trick with sorting on names descendingly. I certainly recommend testing on test files first, not directly in the real environment. Also remember the -WhatIf parameter.
PS C:\temp\dir> (dir -File).Name random_file-01.log random_file-02.log random_file-03.log random_file-04.log random_file-05.log random_file-06.log random_file-07.log random_file-08.log random_file-09.log random_file-10.log random_file-11.log PS C:\temp\dir> dir -File | Sort -Descending Name | Rename-Item -NewName { [regex]::Replace($_.Name, '(\d+)', { $n = [int] $args[0].Groups[1].Value $n += 1 "{0:D2}" -f $n }) } PS C:\temp\dir> (dir -File).Name random_file-02.log random_file-03.log random_file-04.log random_file-05.log random_file-06.log random_file-07.log random_file-08.log random_file-09.log random_file-10.log random_file-11.log random_file-12.log PS C:\temp\dir>
See this StackOverflow article for some more information about the datetime culture stuff. I just pass $null. I've seen [cultureinfo]::InvariantCulture being passed often.
PS C:\temp\dir> 1..8 | %{ $t = $_*7; '' > "FileWithDate-$( (Get-Date).AddDays($t).ToString('yyyy-MM-dd_HH-mm-ss')).log" } # create files with dates PS C:\temp\dir> (dir -File).Name FileWithDate-2016-11-30_02-52-12.log FileWithDate-2016-12-07_02-52-12.log FileWithDate-2016-12-14_02-52-12.log FileWithDate-2016-12-21_02-52-12.log FileWithDate-2016-12-28_02-52-12.log FileWithDate-2017-01-04_02-52-12.log FileWithDate-2017-01-11_02-52-12.log FileWithDate-2017-01-18_02-52-12.log PS C:\temp\dir> # now I want all the dates # shifted 30 days back in time ... PS C:\temp\dir> Get-ChildItem -File | Rename-Item -NewName { [regex]::Replace($_.Name, "\d{4}-\d\d-\d\d_\d\d-\d\d-\d\d", { if ($DateTime = [datetime]::ParseExact($args[0].Value, "yyyy-MM-dd_HH-mm-ss", $null)) { $DateTime = $DateTime.AddDays(-30) $DateTime.ToString('yyyy-MM-dd_HH-mm-ss') } }) } PS C:\temp\dir> (dir -File).Name FileWithDate-2016-10-31_02-52-12.log FileWithDate-2016-11-07_02-52-12.log FileWithDate-2016-11-14_02-52-12.log FileWithDate-2016-11-21_02-52-12.log FileWithDate-2016-11-28_02-52-12.log FileWithDate-2016-12-05_02-52-12.log FileWithDate-2016-12-12_02-52-12.log FileWithDate-2016-12-19_02-52-12.log
PS C:\temp\dir> (dir -File).Name pseudorandom-01.txt pseudorandom-02.txt pseudorandom-03.txt random-06.txt random-07.txt random-08.txt PS C:\temp\dir> Get-ChildItem -File | Rename-Item -NewName { if ($_.Name.StartsWith('pseudo')) { $ext = '.pseudo' } elseif ($_.Name.StartsWith("random")) { $ext = '.rand' } else { $ext = $_.Extension } # no match = keep current ext $_.BaseName + $ext } PS C:\temp\dir> (dir -File).Name pseudorandom-01.pseudo pseudorandom-02.pseudo pseudorandom-03.pseudo random-06.rand random-07.rand random-08.rand
Minimum cookies is the standard setting. This website uses Google Analytics and Google Ads, and these products may set cookies. By continuing to use this website, you accept this.