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
$OriginalFile = "$env:TEMP\staging-draft.txt"
$TargetName = "staging-final.txt"
$null = New-Item -ItemType File -Path $OriginalFile -Force
Rename-Item -Path $OriginalFile -NewName $TargetName -PassThrupwsh ./rename-single-file.ps1 Directory: C:\Users\Tom\AppData\Local\Temp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 9/27/2026 8:29 PM 0 staging-final.txtWhere:
-Path $OriginalFilespecifies the full filesystem path to the file you want to rename.-NewName $TargetNamespecifies the new name and extension only (not a full path).-PassThruoutputs the resultingFileInfoobject to the pipeline.
Verify the rename:
Test-Path "C:\Reports\draft.docx"
Test-Path "C:\Reports\final.docx"False
TrueRenaming 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
Get-ChildItem -Path "C:\Photos" -Filter "*.jpg" | ForEach-Object {
$newName = "vacation_" + $_.Name
Rename-Item -Path $_.FullName -NewName $newName
}./bulk-rename-prefix.ps1
Get-ChildItem "C:\Photos" 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.jpgWhere:
$_.FullNameprovides the current file's full path, required byRename-Item's-Pathparameter.$_.Nameprovides just the filename, used to build the new name with a prefix.
Sequential Renaming with a Counter
$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++
}./bulk-rename-sequential.ps1
Get-ChildItem "C:\Photos" 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.jpgWhere:
{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
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
}pwsh ./bulk-rename-extensions.ps1 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.logWhere:
Get-ChildItem -Filter "*.txt" -Filediscovers only.txtfiles in the target directory.$_.BaseNameisolates the filename without the.txtextension."$($_.BaseName).log"constructs the new filename with the.logextension.Rename-Item -Path $_.FullNametargets 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
Get-ChildItem -Path "C:\Downloads" -Filter "*.pdf" | ForEach-Object {
$newName = $_.Name -replace "[\s_]+", "-"
if ($newName -ne $_.Name) {
Rename-Item -Path $_.FullName -NewName $newName
}
}./regex-clean-names.ps1
Get-ChildItem "C:\Downloads" -Filter "*.pdf" 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.pdfWhere:
-replace "[\s_]+", "-"replaces every run of spaces or underscores with a single hyphen.- The
ifcheck skips files that don't actually need renaming, avoiding unnecessaryRename-Itemcalls.
Reformatting Date Patterns in Filenames
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
}
}./regex-date-reformat.ps1
Get-ChildItem "C:\Reports" -Filter "*.csv" Directory: C:\Reports
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 3/14/2024 8:30 AM 15360 report_2024-03-14.csvWhere:
(\d{2})-(\d{2})-(\d{4})captures the month, day, and year as three separate regex groups.'$3-$1-$2'rearranges those captured groups intoyear-month-dayorder in the replacement string.- Single quotes around the replacement pattern prevent the engine from treating
$3,$1, and$2as 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
$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_' } -PassThrupwsh ./pipeline-regex-rename.ps1 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.csvWhere:
{ $_.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-Itemexecutes 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'
} -WhatIfWhat if: Performing the operation "Rename File" on target "C:\Logs\audit_31-12-2024.log".
New Name: audit_2024-12-31.logPractical 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.
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 $NormalizedNewNameuses case-sensitive inequality to detect files that only differ by casing.[switch]$WhatIfenables safe dry-run previewing.
Execute the script from the console:
pwsh ./normalize-filenames.ps1 -TargetFolder "C:\Scripts\StagingFiles"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\StagingFilesQuick 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 |