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

  1. Work on a copy, or have a backup. See rsync examples.
  2. Dry run (echo mv …, or rename -n).
  3. Use mv -n (no clobber), so two files don’t collapse into one name.
  4. Watch for collisions: lowercasing Photo.jpg and photo.jpg gives the same name.
  5. 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.

  • Linux
  • Bash
  • Command Line