DEV Community

Cover image for PowerShell Rename-Item: Bulk Rename Hundreds of Files with One Line
arnostorg
arnostorg

Posted on

PowerShell Rename-Item: Bulk Rename Hundreds of Files with One Line

You have 240 photos named IMG_0001.JPG through IMG_0240.JPG, or a folder of logs called report final (2).txt, and you want sane names. Clicking through Explorer one file at a time is not a plan. Rename-Item with a script block is.

The one-liner that does the real work

Get-ChildItem *.JPG | Rename-Item -NewName { $_.Name -replace '^IMG_', 'trip-2026-' } -WhatIf
Enter fullscreen mode Exit fullscreen mode

What happens here:

  • Get-ChildItem *.JPG sends file objects down the pipeline, not text.
  • -NewName { ... } is a delay-bind script block: PowerShell runs it once per file, with $_ set to the current file.
  • -replace is a regex replace, so ^IMG_ only matches at the start of the name.
  • -WhatIf prints what would happen. Nothing is renamed yet.

When the preview looks right, run the same line without -WhatIf.

Recipe 1: lowercase extensions

Get-ChildItem -File | Rename-Item -NewName { $_.BaseName + $_.Extension.ToLower() }
Enter fullscreen mode Exit fullscreen mode

BaseName is the name without the extension, so you only touch the .JPG part.

Recipe 2: replace spaces with dashes

Get-ChildItem -File -Filter '* *' | Rename-Item -NewName { $_.Name -replace '\s+', '-' }
Enter fullscreen mode Exit fullscreen mode

Filtering with '* *' first means files without spaces never hit Rename-Item at all, so you avoid "source and destination are the same" errors.

Recipe 3: number files in order

$i = 1
Get-ChildItem *.jpg | Sort-Object LastWriteTime | Rename-Item -NewName {
    'photo-{0:D3}{1}' -f $script:i++, $_.Extension
}
Enter fullscreen mode Exit fullscreen mode

{0:D3} pads the number to three digits (photo-001.jpg), so files sort correctly everywhere. Use $script:i because the script block runs in its own scope; a plain $i++ would not keep counting.

Recipe 4: add a date prefix

Get-ChildItem *.log | Rename-Item -NewName { '{0:yyyy-MM-dd}_{1}' -f $_.LastWriteTime, $_.Name }
Enter fullscreen mode Exit fullscreen mode

ISO dates sort alphabetically and chronologically at the same time.

Three traps worth knowing

  1. -NewName is a name, not a path. Rename-Item cannot move files to another folder. If you want that, use Move-Item.
  2. Square brackets are wildcards. A file called report[1].txt needs -LiteralPath, or pipe it in from Get-ChildItem like above, which avoids the problem.
  3. Collisions stop the rename. If two files would end up with the same name, the second one fails with an error. Always run -WhatIf first and read the output.

Why the pipeline version beats a loop

You could write a foreach loop with Rename-Item -Path $f.FullName -NewName .... It works, but the script-block form keeps the filter, the rule, and the preview in one line you can tweak and rerun. That is the habit that makes PowerShell feel faster than a GUI.

Practice it without breaking real files

Bulk renames are exactly the kind of command you want to try somewhere safe first. In CMD Master's interactive PowerShell lessons you can practice pipelines like this in a live browser terminal with instant feedback, no install and no VM. It also covers Windows CMD and Bash if you jump between systems.

Your turn: what's the messiest batch of filenames you've had to fix? Drop the pattern in the comments and I'll suggest a one-liner.

Top comments (0)