Portrait of Michael Limberger

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.