mv renames one file at a time. When you need to rename hundreds (change extensions, remove spaces, add dates, fix case), use a loop, or a dedicated tool like rename. Always do a dry run first: batch renames are easy to get wrong, and hard to undo.
Method 1: Bash loop with parameter expansion
No extra tools needed:
# change extension .jpeg → .jpg
for f in *.jpeg; do
mv -n -- "$f" "${f%.jpeg}.jpg"
done
${f%.jpeg}removes the suffix.jpeg.-n= don’t overwrite existing files.--protects names starting with-.- Quote
"$f", so names with spaces work.
Useful expansions:
| Expansion | Result for f=report 2026.txt |
|---|---|
${f%.txt} |
report 2026 (remove suffix) |
${f#report } |
2026.txt (remove prefix) |
${f// /_} |
report_2026.txt (replace all spaces) |
${f,,} |
report 2026.txt lowercased |
${f^^} |
uppercased |
Dry run: put echo in front of mv, check the output, then remove it:
for f in *.JPG; do echo mv -n -- "$f" "${f%.JPG}.jpg"; done
More examples:
# replace spaces with underscores
for f in *\ *; do mv -n -- "$f" "${f// /_}"; done
# add a date prefix
d=$(date +%F); for f in *.log; do mv -n -- "$f" "${d}_$f"; done
# lowercase all names
for f in *; do mv -n -- "$f" "${f,,}"; done
Method 2: the rename command (Perl regex)
On Debian/Ubuntu, install the Perl-based rename:
sudo apt install rename
Syntax: rename [options] 's/search/replace/' files. Use -n for a dry run:
rename -n 's/\.jpeg$/.jpg/' *.jpeg # preview
rename 's/\.jpeg$/.jpg/' *.jpeg # do it
rename 's/ /_/g' * # spaces → underscores
rename 'y/A-Z/a-z/' * # lowercase
rename 's/^/backup_/' *.sql # add a prefix
rename 's/(\d{4})(\d{2})(\d{2})/$1-$2-$3/' * # 20260101 → 2026-01-01
The regex is Perl syntax. See regex basics.
Note: on RHEL, AlmaLinux and CentOS, rename is a simpler util-linux version: rename .jpeg .jpg *.jpeg (plain string replacement, no regex). Install prename from EPEL for the Perl version.
Method 3: recursive renames with find
Process files in subdirectories:
find /srv/media -type f -name '*.JPG' -exec rename -n 's/\.JPG$/.jpg/' {} +
When renaming directories recursively, rename the deepest ones first (find … -depth), so parent paths don’t change under the command’s feet.
Method 4: mmv and vidir
mmv: pattern-based moves,mmv '*.jpeg' '#1.jpg'.vidir(from moreutils): opens a list of file names in your editor. Edit the names, save, and the files get renamed. Very handy for irregular renames.
sudo apt install moreutils
vidir ./photos
Undo a batch rename
If something went wrong, a reverse rename may fix it, as long as no names collided. Keep a log while renaming, so you can reverse it:
for f in *.jpeg; do n="${f%.jpeg}.jpg"; mv -n -- "$f" "$n" && printf '%s\t%s\n' "$n" "$f" >> rename.log; done
# undo:
while IFS=$'\t' read -r new old; do mv -n -- "$new" "$old"; done < rename.log
Safety checklist
- Work on a copy, or have a backup. See rsync examples.
- Dry run (
echo mv …, orrename -n). - Use
mv -n(no clobber), so two files don’t collapse into one name. - Watch for collisions: lowercasing
Photo.jpgandphoto.jpggives the same name. - Check that scripts, configs and web links don’t still use the old names (or add redirects for websites).
Quick reference
| Task | Command |
|---|---|
| Change extension | for f in *.a; do mv -n -- "$f" "${f%.a}.b"; done |
| Regex rename | rename 's/old/new/' files |
| Preview | rename -n … / echo mv … |
| Lowercase | rename 'y/A-Z/a-z/' * |
| Recursive | find … -exec rename … {} + |
| Edit names in an editor | vidir |
Related: Linux find command and wildcards and globbing.