Rename multiple files at once
Apply a rename rule (extension swap, prefix add, regex replace) to a batch of files in one shell-native pass — no GUI, no third-party tool.
How to rename multiple files at once in each shell
for f in *.txt; do mv "$f" "${f%.txt}.md"; done`${f%.txt}` strips the trailing `.txt` extension. Always quote `"$f"` to handle filenames with spaces. For a regex-style rename across recursive trees, use the Perl-based `rename` utility (Debian/Ubuntu): `rename 's/\.txt$/\.md/' **/*.txt`. Note: macOS Homebrew installs a DIFFERENT `rename` (util-linux variant) with incompatible syntax — `brew install rename` then check `rename --help`.
for f in *.txt; do mv "$f" "${f%.txt}.md"; doneSame loop syntax as bash. Zsh's built-in `zmv` is the idiomatic alternative: `autoload -U zmv; zmv '(*).txt' '$1.md'`. Capture groups in parentheses, refer to them as `$1`/`$2`. `zmv -n` for dry-run (prints what WOULD happen without doing it) — the canonical safety net.
for f in *.txt; mv $f (string replace ".txt" ".md" $f); endFish has no `${var%pattern}` parameter expansion — use `string replace` instead. For regex: `string replace -r '\.txt$' '.md' $f`. Fish lacks a built-in `zmv` equivalent — for complex rename batches, fall back to a one-liner with `find` + `mv`, or install a helper like `rename`.
Get-ChildItem *.txt | Rename-Item -NewName { $_.Name -replace '\.txt$', '.md' }The `-NewName` parameter accepts a script block (the `{ ... }` form) — `$_` is the current pipeline item. `-replace` is regex (so `.txt$` matches end-of-name, `\.txt$` matches the literal dot). Add `-WhatIf` for dry-run: `Rename-Item ... -WhatIf` prints the planned operations without executing. `-Confirm` prompts per file. Recursive: `Get-ChildItem -Recurse *.txt | Rename-Item ...`.
for %f in (*.txt) do ren "%f" "%~nf.md"`%~nf` is the filename WITHOUT extension. `%~xf` is just the extension. cmd has no regex-rename — for anything beyond extension swaps, shell out to pwsh: `powershell -NoProfile -Command "Get-ChildItem *.txt | Rename-Item ..."`. Inside a `.bat` file, double the `%` signs: `for %%f in (*.txt) do ren "%%f" "%%~nf.md"`.
Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.
Gotchas & notes
- Always dry-run a batch rename FIRST. bash: `for f in *.txt; do echo mv "$f" "${f%.txt}.md"; done` (prefix with `echo`). zsh `zmv -n`. pwsh `-WhatIf`. cmd `for ... do echo ren ...`. The 30 seconds you spend verifying the planned operations saves hours when a typo would have nuked filenames into garbage.
- pwsh `Rename-Item` errors if the destination filename already exists. To overwrite, pipe through `Remove-Item` first or use `Move-Item -Force`. The bash `mv` clobbers by default; use `mv -i` for interactive prompt or `mv -n` for "no-clobber" (skip if exists).
- For renames that need to change the directory part (move + rename in one), bash and pwsh both have it covered (`mv old/file new/path`, `Rename-Item` does NOT cross directories — use `Move-Item` for that). cmd `ren` is rename-only (same directory); use `move` for cross-directory.
- Filenames with unusual characters (spaces, brackets, dollar signs, newlines) are the #1 source of bash rename loop bugs. The safest portable form uses null-terminated input: `find . -name "*.txt" -print0 | while IFS= read -r -d "" f; do mv "$f" "${f%.txt}.md"; done`. pwsh handles all of these natively because it passes file objects through the pipeline, not strings.
Related commands
Related tasks
- Find and replace text in files— Substitute one string for another inside a file (or every file in a tree), in place.
- Find files by name— Locate files matching a glob pattern (e.g. `*.log`) anywhere under the current tree.
- List files recursively— Print every file under a directory tree, optionally as a flat list of full paths.