Renaming a single file is easy using Rename-Item, but sometimes cmdlet parameter can be much smarter and help you automate in bulk.

For example, let’s assume you have a load of pictures in your picture folder:

$path = [Environment]::GetFolderPath('MyPictures')
Get-ChildItem -Path $path -Filter *.png

If you wanted to rename all of them, i.e. by prepending a number, you would have to program a loop, like so:

$path = [Environment]::GetFolderPath('MyPictures')
$counter = 0
$files = Get-ChildItem -Path $path -Filter *.png 
$files |
  ForEach-Object {
    $counter++
    $newname = '{0} - {1}' -f $counter, $_.Name
    Rename-Item -Path $_.FullName -NewName $newname  -WhatIf 
  }

There is a much easier solution: the -NewName parameter also accepts a script block, and the script block will be executed for each element that is piped into Rename-Item, simplifying the code to:

$path = [Environment]::GetFolderPath('MyPictures')
$counter = 0
$files = Get-ChildItem -Path $path -Filter *.png 
$files | Rename-Item -NewName { 
    $script:counter++
    '{0} - {1}' -f $counter, $_.Name 
    } -WhatIf

There is just one important difference: the script block executed by Rename-Item runs in its own scope, so if you wanted to use an incrementing counter, you would need to add “script:” to your variable name so that the variable is used in script scope.

Warning: There is a nasty bug in some PowerShell versions where renaming files will change the output of Get-ChildItem, so if you pipe the result of Get-ChildItem directly into Rename-Item, you may end up in an endless loop where files are renamed over and over again until the maximum file path length is exceeded. To play safe, always store the results of Get-ChildItem in a variable before you submit the variable to Rename-Item!

Twitter This Tip! ReTweet this Tip!

Anonymous