The mv Loop Gets Old Fast
You’ve been there. Three files to rename, no problem, just mv them one at a time. But somewhere around file twelve, you realize you’re doing something a computer was literally built for, and you’re doing it manually like a cursed goblin.
# Fine for 3 files. Miserable for 300.mv old_report_1.txt new_report_1.txtmv old_report_2.txt new_report_2.txt# ... kill meLinux has several proper tools for bulk renaming. Which one to reach for depends on whether you want regex power, visual editing, or glob patterns. Here’s the breakdown.
rename, Regex Renaming (the Perl One)
The rename command on Debian/Ubuntu systems is Perl-based and takes a Perl substitution expression. It’s the heavy hitter for pattern-based renames.
# Install on Debian/Ubuntusudo apt install rename
# Basic usage: replace 'foo' with 'bar' in all .txt filenamesrename 's/foo/bar/g' *.txt
# Always dry-run first — see what would happen without doing itrename -n 's/foo/bar/g' *.txtCommon patterns you’ll actually use:
# Lowercase all filenamesrename 's/(.*)/\L$1/' *
# Replace spaces with underscoresrename 's/ /_/g' *
# Change extension from .jpeg to .jpgrename 's/\.jpeg$/.jpg/' *.jpeg
# Add a prefix to all filesrename 's/^/2024-/' *.txt
# Strip a prefixrename 's/^IMG_//' *.jpg
# Recursive rename (using find to feed it)find . -name "* *" -exec rename 's/ /_/g' {} +Wait, Which rename Do You Have?
Here’s the annoying part: there are two rename commands. The Perl version (above) and a simpler util-linux version on some systems. Check which you have:
rename --versionIf you see rename from util-linux, the syntax is completely different, it takes a fixed string substitution, not regex: rename old new files. The Perl version instead prints a version line from the File::Rename Perl module (e.g. /usr/bin/rename using File::Rename version 2.xx).
On Fedora/RHEL, the Perl version is called prename. On Arch it’s available as perl-rename. Worth knowing before you copy-paste a regex from Stack Overflow and wonder why nothing happened.
vidir, Edit Filenames Like a Text File
This one is genuinely underrated. vidir (from the moreutils package) dumps all filenames into your $EDITOR, you edit them however you want, save and quit, and it renames everything accordingly.
sudo apt install moreutils
# Rename files in current directoryvidir
# Rename specific filesls *.jpg | vidir -
# Rename recursively (careful)find . -type f | vidir -What opens is something like:
1 ./IMG_001.jpg2 ./IMG_002.jpg3 ./IMG_003.jpgChange the names on the right side. Delete a line entirely to delete that file. Reorder lines, vidir handles it. Save, quit, done.
This is hands-down the best tool when you have a complex rename you want to think about. You can use all your editor tricks, multi-cursor, regex replace across the buffer, whatever. Nothing changes until you save. Close without saving and nothing happens at all.
Use this for: renaming a batch of photos after a trip, reorganizing project files, any rename where you want human eyes on each decision.
mmv, Glob-Based Mass Rename
mmv (mass move) uses shell glob patterns with numbered back-references. Less common but handy when you’re thinking in glob terms rather than regex.
sudo apt install mmv
# Rename all .txt files to .bakmmv '*.txt' '#1.bak'
# Add a prefixmmv '*.jpg' 'vacation_#1.jpg'
# Change double extensionmmv '*.tar.gz' '#1.tgz'The #1 refers to what matched the first *. It’s not as powerful as Perl regex but the syntax is more readable for simple transformations.
fd + -x, Find and Rename in One Shot
fd is the modern find replacement, and its -x flag executes a command per result with some nice placeholder variables.
sudo apt install fd-find# On some systems it's 'fdfind', alias it: alias fd=fdfind
# Change .jpeg to .jpg for all files recursivelyfd --extension jpeg -x mv {} {.}.jpg
# Remove spaces from filenames found recursivelyfd ' ' -x bash -c 'mv "$1" "${1// /_}"' _ {}Placeholders: {} is the full path, {.} strips the extension, {/} is the filename only, {//} is the parent directory. The -x flag handles filenames with spaces safely, no quoting headaches.
zmv, zsh Users, You Have This Already
If you’re on zsh, zmv is built in (load it with autoload -U zmv). It uses zsh’s extended glob syntax.
autoload -U zmv
# Change extensionzmv '(*).txt' '$1.md'
# Add prefixzmv '(*).jpg' 'photo_$1.jpg'
# Dry-runzmv -n '(*).txt' '$1.md'It’s not installed separately, it’s just there waiting. Nice when you’re already in zsh and don’t want to install anything.
Practical Scenarios
Rename 500 Photos to a Consistent Format
You’ve got IMG_0001.jpg through IMG_0500.jpg and want 2024-01-0001.jpg etc:
rename 's/IMG_/2024-01-/' IMG_*.jpgFor more complex date-based renaming with exiftool:
# Rename by EXIF date — different tool but worth knowingexiftool '-FileName<DateTimeOriginal' -d '%Y-%m-%d_%H%M%S.%%e' *.jpgRemove Spaces from Filenames Recursively
Classic problem. Two approaches:
# Using find + rename (Perl version)find . -name "* *" -exec rename 's/ /_/g' {} +
# Using fdfd ' ' -x bash -c 'mv "$1" "${1// /_}"' _ {}Change Extension for All Files in a Directory
# rename (Perl)rename 's/\.txt$/.md/' *.txt
# Pure bash (no extra tools needed)for f in *.txt; do mv "$f" "${f%.txt}.md"; doneComplex Rename You Want to Review
vidirOpen it, take your time, edit like a document, save when you’re confident.
Quick Reference
| Tool | Best For | Install |
|---|---|---|
rename (Perl) | Regex-based renames, extensions, prefixes | apt install rename |
vidir | Visual/manual review, complex batches | apt install moreutils |
mmv | Glob patterns with back-references | apt install mmv |
fd -x | Find + rename pipeline, recursive ops | apt install fd-find |
zmv | zsh users, already available | autoload -U zmv |
for loop | Simple, no deps, 5-10 files | Built-in bash |
The right tool depends on the job. Regex substitution across 500 files? rename. Batch of photos you want to manually review? vidir. Already in a fd pipeline? Use -x. Already in zsh? zmv is sitting right there.
Dry-run everything first. rename -n shows you what would happen. vidir shows you the changes before you save. mmv has a -n flag too. Your future self, the one who has to explain to a client why all their files are now named undefined, will thank you.