Renaming every .txt to .md, or every .jpeg to .jpg — PowerShell can do this in one line without any loops. Here are the patterns, from the simplest to the most flexible.

The One-Liner

Get-ChildItem -File | Rename-Item -NewName { $_.Name -replace '\.txt$', '.md' }

This renames every file in the current directory whose name ends in .txt, changing the extension to .md. Files with other extensions are left unchanged.

Breaking it down:

  • Get-ChildItem -File — lists files only (no subdirectories).
  • Rename-Item -NewName { ... } — the script block receives each file object as $_ and returns the new name.
  • $_.Name -replace '\.txt$', '.md' — the -replace operator uses a regex. The \. escapes the dot (a bare . in regex means “any character”), and $ anchors the match to the end of the string so it only touches the extension.

Preview Before Renaming

Add -WhatIf to do a dry run — PowerShell prints what it would rename without touching anything:

Get-ChildItem -File | Rename-Item -NewName { $_.Name -replace '\.txt$', '.md' } -WhatIf

Output:

What if: Performing the operation "Rename File" on target "Item: C:\notes\todo.txt Destination: C:\notes\todo.md".
What if: Performing the operation "Rename File" on target "Item: C:\notes\readme.txt Destination: C:\notes\readme.md".

Always run with -WhatIf first when working on a directory you care about.

Specifying a Path

Run from any directory by passing -Path:

Get-ChildItem -Path 'C:\notes' -File | Rename-Item -NewName { $_.Name -replace '\.txt$', '.md' }

Recursive - All Subdirectories

Add -Recurse to walk the entire tree:

Get-ChildItem -Path 'C:\notes' -File -Recurse | Rename-Item -NewName { $_.Name -replace '\.txt$', '.md' }

Filter First with -Filter

-Filter is faster than -Include because it’s handled by the filesystem rather than PowerShell. Use it when you want to limit which files are even collected:

Get-ChildItem -File -Filter '*.txt' | Rename-Item -NewName { $_.Name -replace '\.txt$', '.md' }

-replace vs .Replace()

The -replace operator is regex-based. The string .Replace() method is a literal find-and-replace:

# Regex   - only replaces .txt at the end of the name
$_.Name -replace '\.txt$', '.md'

# Literal - replaces every occurrence of ".txt" anywhere in the name
$_.Name.Replace('.txt', '.md')

For extensions, the regex version is safer: .Replace('.txt', '.md') would also rename a file called notes.txt.backup to notes.md.backup, which is probably not what you want.

Using ForEach-Object for More Control

When you need to do more than just rename — log each operation, skip certain files conditionally, or act on the full path — reach for ForEach-Object:

Get-ChildItem -File -Filter '*.txt' | ForEach-Object {
    $newName = $_.Name -replace '\.txt$', '.md'
    Rename-Item -Path $_.FullName -NewName $newName
}

This is equivalent to the one-liner but gives you room to add logic inside the block.