Skip to content

PowerShell Renaming Files

How to rename files and modify extensions in PowerShell using Rename-Item, regex patterns, and bulk operations for efficient file management.

Renaming files and modifying extensions in PowerShell is performed using the Rename-Item cmdlet (aliases: ren, rni). Unlike traditional batch commands (such as ren or rename), PowerShell can manipulate filenames using string manipulation methods, pipeline binding, and regular expression patterns with the -replace operator, enabling powerful single-line bulk renaming operations. Rename-Item changes the name of a file or folder without moving it to a different location; combined with the pipeline and regex, it becomes a powerful tool for bulk-renaming large batches of files according to consistent patterns.

The Rename-Item Cmdlet

Basic Syntax

Rename-Item -Path <String> -NewName <String> [-Force] [-PassThru] [-WhatIf] [-Confirm]

Renaming a Single File

rename-single-file.ps1powershell
$OriginalFile = "$env:TEMP\staging-draft.txt"
$TargetName   = "staging-final.txt"

$null = New-Item -ItemType File -Path $OriginalFile -Force

Rename-Item -Path $OriginalFile -NewName $TargetName -PassThru
Consolepowershell
pwsh ./rename-single-file.ps1
Outputplaintext
    Directory: C:\Users\Tom\AppData\Local\Temp

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           9/27/2026  8:29 PM              0 staging-final.txt

Where:

  • -Path $OriginalFile specifies the full filesystem path to the file you want to rename.
  • -NewName $TargetName specifies the new name and extension only (not a full path).
  • -PassThru outputs the resulting FileInfo object to the pipeline.

Verify the rename:

Test-Path "C:\Reports\draft.docx"
Test-Path "C:\Reports\final.docx"
False
True

Renaming via the Pipeline

Rename-Item also accepts input directly from the pipeline, useful when the target item is already the result of another command.

Get-Item "C:\Reports\final.docx" | Rename-Item -NewName "Q1-Final.docx"

Bulk Renaming with Pipeline + ForEach-Object

For renaming many files at once, pipe Get-ChildItem into ForEach-Object, computing a new name for each file individually. Inside the loop, you can calculate new filenames using string interpolation, timestamps, or object properties like $_.BaseName.

Example: Adding a Prefix

bulk-rename-prefix.ps1powershell
Get-ChildItem -Path "C:\Photos" -Filter "*.jpg" | ForEach-Object {
    $newName = "vacation_" + $_.Name
    Rename-Item -Path $_.FullName -NewName $newName
}
Consolepowershell
./bulk-rename-prefix.ps1
Get-ChildItem "C:\Photos"
Outputplaintext
    Directory: C:\Photos

Mode                  LastWriteTime          Length Name
----                  -------------          ------ ----
-a---           3/14/2024   8:30 AM         204800 vacation_img001.jpg
-a---           3/14/2024   8:31 AM         198400 vacation_img002.jpg

Where:

  • $_.FullName provides the current file's full path, required by Rename-Item's -Path parameter.
  • $_.Name provides just the filename, used to build the new name with a prefix.

Sequential Renaming with a Counter

bulk-rename-sequential.ps1powershell
$counter = 1

Get-ChildItem -Path "C:\Photos" -Filter "*.jpg" | ForEach-Object {
    $newName = "photo_{0:D3}.jpg" -f $counter
    Rename-Item -Path $_.FullName -NewName $newName
    $counter++
}
Consolepowershell
./bulk-rename-sequential.ps1
Get-ChildItem "C:\Photos"
Outputplaintext
    Directory: C:\Photos

Mode                  LastWriteTime         Length Name
----                  -------------         ------ ----
-a---           3/14/2024   8:30 AM         204800 photo_001.jpg
-a---           3/14/2024   8:31 AM         198400 photo_002.jpg

Where:

  • {0:D3} formats the counter with leading zeros to three digits.
  • $counter++ increments the counter after each rename, ensuring sequential, non-repeating filenames.

Example: Batch Extension Replacement

bulk-rename-extensions.ps1powershell
param (
    [string]$TargetFolder = "$env:TEMP\LogBatch"
)

$null = New-Item -ItemType Directory -Path $TargetFolder -Force
$null = New-Item -ItemType File -Path "$TargetFolder\trace1.txt", "$TargetFolder\trace2.txt" -Force

Get-ChildItem -Path $TargetFolder -Filter "*.txt" -File | ForEach-Object {
    $NewFileName = "$($_.BaseName).log"
    Rename-Item -Path $_.FullName -NewName $NewFileName -PassThru
}
Consolepowershell
pwsh ./bulk-rename-extensions.ps1
Outputplaintext
    Directory: C:\Users\Tom\AppData\Local\Temp\LogBatch

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           9/27/2026  8:52 PM              0 trace1.log
-a---           9/27/2026  8:52 PM              0 trace2.log

Where:

  • Get-ChildItem -Filter "*.txt" -File discovers only .txt files in the target directory.
  • $_.BaseName isolates the filename without the .txt extension.
  • "$($_.BaseName).log" constructs the new filename with the .log extension.
  • Rename-Item -Path $_.FullName targets each file by its absolute path.

Renaming Using Regex Replace Patterns

For more complex renaming logic (like removing unwanted characters, reformatting date patterns, or restructuring filenames) you can combine the -replace operator with Rename-Item.

Removing Unwanted Characters

regex-clean-names.ps1powershell
Get-ChildItem -Path "C:\Downloads" -Filter "*.pdf" | ForEach-Object {
    $newName = $_.Name -replace "[\s_]+", "-"
    if ($newName -ne $_.Name) {
        Rename-Item -Path $_.FullName -NewName $newName
    }
}
Consolepowershell
./regex-clean-names.ps1
Get-ChildItem "C:\Downloads" -Filter "*.pdf"
Outputplaintext
    Directory: C:\Downloads

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           3/14/2024   8:30 AM         102400 annual-report-2024.pdf
-a---           3/14/2024   8:31 AM          88320 invoice-march.pdf

Where:

  • -replace "[\s_]+", "-" replaces every run of spaces or underscores with a single hyphen.
  • The if check skips files that don't actually need renaming, avoiding unnecessary Rename-Item calls.

Reformatting Date Patterns in Filenames

regex-date-reformat.ps1powershell
Get-ChildItem -Path "C:\Reports" -Filter "*.csv" | ForEach-Object {
    if ($_.Name -match "(\d{2})-(\d{2})-(\d{4})") {
        $newName = $_.Name -replace "(\d{2})-(\d{2})-(\d{4})", '$3-$1-$2'
        Rename-Item -Path $_.FullName -NewName $newName
    }
}
Consolepowershell
./regex-date-reformat.ps1
Get-ChildItem "C:\Reports" -Filter "*.csv"
Outputplaintext
    Directory: C:\Reports

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           3/14/2024   8:30 AM          15360 report_2024-03-14.csv

Where:

  • (\d{2})-(\d{2})-(\d{4}) captures the month, day, and year as three separate regex groups.
  • '$3-$1-$2' rearranges those captured groups into year-month-day order in the replacement string.
  • Single quotes around the replacement pattern prevent the engine from treating $3, $1, and $2 as PowerShell variables. Instead, these placeholders must be passed literally to the regex engine for proper substitution.

Stripping a Prefix with Regex

Get-ChildItem -Path "C:\Exports" -Filter "OLD_*.xlsx" | ForEach-Object {
    $newName = $_.Name -replace "^OLD_", ""
    Rename-Item -Path $_.FullName -NewName $newName
}

Where ^OLD_ matches the literal text OLD_ only at the start of the filename, thanks to the ^ anchor.

Dynamic Renaming via Script Blocks and Regex (-replace)

PowerShell supports delay-bind script blocks on Rename-Item. This feature lets you pipe items directly into Rename-Item and calculate the -NewName dynamically inside a script block ({ }) using the -replace operator without needing an explicit ForEach-Object loop.

Direct Pipeline Regex Renaming

pipeline-regex-rename.ps1powershell
$TargetFolder = "$env:TEMP\ReportDrop"

$null = New-Item -ItemType Directory -Path $TargetFolder -Force
$null = New-Item -ItemType File -Path "$TargetFolder\report-2024-q1.csv", "$TargetFolder\report-2024-q2.csv" -Force

Get-ChildItem -Path $TargetFolder -Filter "report-*.csv" |
    Rename-Item -NewName { $_.Name -replace '^report-', 'FINANCE_REPORT_' } -PassThru
Consolepowershell
pwsh ./pipeline-regex-rename.ps1
Outputplaintext
    Directory: C:\Users\Tom\AppData\Local\Temp\ReportDrop

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           9/27/2026  8:54 PM              0 FINANCE_REPORT_2024-q1.csv
-a---           9/27/2026  8:54 PM              0 FINANCE_REPORT_2024-q2.csv

Where:

  • { $_.Name -replace '^report-', 'FINANCE_REPORT_' } is a delay-bind script block evaluated for each incoming pipeline object.
  • ^report- is a regular expression pattern anchored to the start of the string (^).
  • Rename-Item executes the substitution directly on each file.

Advanced Regex: Reordering Filename Tokens

You can use regular expression capture groups ($1, $2) to reorder parts of filenames, such as changing date formats from DD-MM-YYYY to ISO standard YYYY-MM-DD:

Get-ChildItem -Filter "audit_*.log" | Rename-Item -NewName {
    $_.Name -replace 'audit_(\d{2})-(\d{2})-(\d{4})\.log', 'audit_$3-$2-$1.log'
} -WhatIf
Outputplaintext
What if: Performing the operation "Rename File" on target "C:\Logs\audit_31-12-2024.log".
New Name: audit_2024-12-31.log

Practical Example: File Normalization Engine

The following production script scans a directory for files containing spaces, invalid casing, or non-standard characters, normalizes them into lowercase hyphen-delimited names, and outputs an audit log of all changes.

normalize-filenames.ps1powershell
param (
    [string]$TargetFolder = "$PSScriptRoot\StagingFiles",
    [switch]$WhatIf
)

if (-not (Test-Path -Path $TargetFolder -PathType Container)) {
    Write-Error "Target directory '$TargetFolder' does not exist."
    exit 1
}

$CandidateFiles = Get-ChildItem -Path $TargetFolder -File

$RenamedLog = foreach ($file in $CandidateFiles) {
    $CleanBaseName = $file.BaseName.Trim() -replace '\s+', '-' -replace '[^a-zA-Z0-9_-]', ''
    $NormalizedNewName = "$($CleanBaseName.ToLower())$($file.Extension.ToLower())"

    if ($file.Name -cne $NormalizedNewName) {
        if ($WhatIf) {
            Rename-Item -Path $file.FullName -NewName $NormalizedNewName -WhatIf
            $actionResult = "Simulated"
        }
        else {
            Rename-Item -Path $file.FullName -NewName $NormalizedNewName -Force
            $actionResult = "Renamed"
        }

        [PSCustomObject]@{
            OriginalName = $file.Name
            NewName      = $NormalizedNewName
            Status       = $actionResult
            Directory    = $file.DirectoryName
        }
    }
}

if ($RenamedLog) {
    $RenamedLog | Format-Table -AutoSize
} else {
    Write-Output "All files are already normalized."
}

Where:

  • Trim() removes leading and trailing whitespace.
  • -replace '\s+', '-' replaces consecutive whitespace characters with a single hyphen.
  • -replace '[^a-zA-Z0-9_-]', '' strips invalid or non-standard characters.
  • .ToLower() normalizes both the filename and its extension to lowercase.
  • $file.Name -cne $NormalizedNewName uses case-sensitive inequality to detect files that only differ by casing.
  • [switch]$WhatIf enables safe dry-run previewing.

Execute the script from the console:

pwsh ./normalize-filenames.ps1 -TargetFolder "C:\Scripts\StagingFiles"
Outputplaintext
OriginalName               NewName                    Status  Directory
------------               -------                    ------  ---------
Client Report 2024 #1.PDF  client-report-2024-1.pdf   Renamed C:\Scripts\StagingFiles
Data_Export (Final).CSV    data_export-final.csv      Renamed C:\Scripts\StagingFiles

Quick Reference

Task Command
Rename a single file Rename-Item -Path "C:\Temp\old.txt" -NewName "new.txt"
Rename via pipeline Get-Item "C:\Temp\old.txt" | Rename-Item -NewName "new.txt"
Bulk rename with prefix Get-ChildItem *.jpg | ForEach-Object { Rename-Item -Path $_.FullName -NewName "prefix_$($_.Name)" }
Sequential rename $i=1; Get-ChildItem *.jpg | ForEach-Object { Rename-Item -Path $_.FullName -NewName "photo_{0:D3}.jpg" -f $i; $i++ }
Change extension Get-ChildItem *.txt | ForEach-Object { Rename-Item -Path $_.FullName -NewName "$($_.BaseName).log" }
Regex replace in name Get-ChildItem *.pdf | ForEach-Object { Rename-Item -Path $_.FullName -NewName ($_.Name -replace ' ', '-') }
Direct pipeline regex rename Get-ChildItem *.csv | Rename-Item -NewName { $_.Name -replace '^report-', 'FINANCE_' }
Preview rename Rename-Item -Path ... -NewName ... -WhatIf
Overwrite existing Rename-Item -Path ... -NewName ... -Force
Navigation