Michael Limberger
Need me? Email mike@limberger.ca
Regex
Rename - Bulk File Operations
Hundreds of names, one pattern
The Perl rename command transforms filenames using regex. One command can rename hundreds of files following a pattern.
Show me. The shape.
rename [options] 's/PATTERN/REPLACEMENT/' files
Always dry-run
ALWAYS test with -n (dry run) first:
rename -n 's/old/new/' *.txt
This shows what WOULD happen without actually renaming anything. Only remove -n when you are sure it is right.
Basic replacements
Show me.
rename 's/photo/image/' *.jpg
Before: photo_001.jpg, photo_002.jpg
After: image_001.jpg, image_002.jpg
The s/OLD/NEW/ syntax is Perl's substitution operator. s for substitute. Slashes are the delimiters.
Case
Lowercase all filenames with rename 'y/A-Z/a-z/' *.
Uppercase them with rename 'y/a-z/A-Z/' *.
The y/// form is Perl's transliteration operator, character by character.
Deleting pieces
rename 's/_backup//' *.txt
# report_backup.txt becomes report.txt
rename 's/ //g' *
The g flag means global: replace ALL occurrences, not just the first.
Spaces to underscores
rename 's/ /_/g' *
# file with spaces.txt becomes file_with_spaces.txt
rename 's/ /-/g' *
Extensions
Rename .jpeg to .jpg:
rename 's/\.jpeg$/.jpg/' *.jpeg
Escape the dot (\.) and anchor to the end ($). Without the anchor, "my.jpeg.photo.jpeg" would become "my.jpg.photo.jpeg" after the first replacement.
rename 's/\.txt$/.md/' *.txt
rename 's/\.htm$/.html/' *.htm
Prefixes and suffixes
Add a prefix: rename 's/^/prefix_/' * turns file.txt into prefix_file.txt. The ^ means start of filename.
Insert before the extension:
rename 's/(\.\w+)$/_backup$1/' *
# report.txt becomes report_backup.txt
Capture groups get a full walk in the next part. $1 is "whatever the first pair of parentheses caught."
Remove IMG_ from camera files: rename 's/^IMG_//' IMG_* turns IMG_2024_06_15.png into 2024_06_15.png.
Numbering
Sequential numbering is a bit more complex. A counter, evaluated as Perl:
rename 's/^/sprintf("%03d_", ++$::count)/e' *
The /e flag evaluates Perl code in the replacement. apple.txt, banana.txt, cherry.txt become 001_apple.txt, 002_banana.txt, 003_cherry.txt.
g and i
Without g, only the first match is replaced: s/_/-/ on a_b_c.txt gives a-b_c.txt. With g: a-b-c.txt.
Case-insensitive: rename 's/PHOTO/image/i' * matches PHOTO, Photo, photo, pHoTo.
Jobs you will actually run
Show me.
rename 's/^DSC_?/photo_/' DSC*
rename 's/^\d{13}_//' *.png
rename 's/^/2024-01-15_/' *.txt
rename 's/\s*\(copy\)//i' *
rename 's/\s*-\s*Copy//i' *
rename 's/_\d+(?=\.\w+$)//' *
Camera prefixes. Timestamp prefixes on AI images. Date prefixes. "(copy)" and "- Copy" junk. Trailing numbers before the extension. That last one is a lookahead: match digits only if an extension follows.
Habits
Always use -n first. Always. Test on a copy if you are nervous. Use single quotes around the pattern so the shell does not expand it. If a pattern does not match, the file is not renamed. No error. Process files in stages. Simple patterns, one step at a time.