Michael Limberger
Need me? Email mike@limberger.ca
Regex
Rename - Pattern Capture Magic
Catch a piece, put it back elsewhere
This is where rename gets seriously powerful. Capture groups let you rearrange, transform, and restructure filenames. Parentheses in regex capture what they match.
Show me.
Pattern: (\d+)
Input: photo_123.jpg
Capture: $1 = "123"
Pattern: (\w+)_(\d+)\.(\w+)
Input: photo_123.jpg
Captures: $1 = "photo", $2 = "123", $3 = "jpg"
In the replacement, use $1, $2, $3. Dollar one, dollar two, dollar three.
Swap the parts
rename 's/(\w+)_(\d+)/$2_$1/' *.jpg
Before: photo_001.jpg
After: 001_photo.jpg
"photo" is $1, "001" is $2, then they come back in reverse order.
Dates that humans can read
rename 's/(\d{2})(\d{2})(\d{4})/$3-$1-$2/' *
Before: 01152024_report.txt
After: 2024-01-15_report.txt
Each \d{2} or \d{4} captures its digits into numbered groups.
Pad the numbers
rename 's/(\d+)/sprintf("%03d", $1)/e' *
Before: file1.txt, file2.txt, file10.txt
After: file001.txt, file002.txt, file010.txt
The /e flag evaluates Perl code. sprintf formats the number. (\d+) captures any number. "%03d" means a decimal integer, at least three digits, pad with zeros.
Keep only the number
rename 's/.*_(\d+)\.(.+)/$1.$2/' *
Before: random_garbage_text_042.jpg
After: 042.jpg
The .* eats everything up to the last underscore before the number.
Camera files
Camera files often look like IMG_20240115_143022.jpg. That is IMG_YYYYMMDD_HHMMSS.
Show me.
rename 's/IMG_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})/$1-$2-$3_$4-$5-$6/' IMG_*
Before: IMG_20240115_143022.jpg
After: 2024-01-15_14-30-22.jpg
Group without capturing
Sometimes you need to group but not capture. Use (?:...)
rename 's/(?:IMG_|DSC_)(\d+)/$1/' *
Matches either IMG_ or DSC_ but does not capture it. Only the number goes into $1.
A little Perl in the replacement
rename 's/(\d+)/($1 < 100 ? sprintf("0%02d", $1) : $1)/e' *
This pads numbers under 100 differently. Probably overkill, but it shows what is possible. The ? : is Perl's ternary: if, then, else.
AI-generated images
Stable Diffusion often outputs files like:
00001-1234567890-masterpiece_best_quality.png
Dry run first:
rename -n 's/^(\d+)-(\d+)-(.+)\.png$/$1_$3.png/' *.png
rename -n 's/_/ /g' *
rename -n 's/\s+/_/g' *
rename 's/^(\d+)-\d+-(.+)\.png$/image_$1.png/' *.png
Mac screenshots
Mac screenshots: "Screenshot 2024-01-15 at 2.30.22 PM.png". Clean format: "2024-01-15-1430.png".
rename 's/Screenshot (\d{4})-(\d{2})-(\d{2}) at (\d+)\.(\d+)\.(\d+) (AM|PM)\.png/$1-$2-$3-$4$5.png/' Screenshot*
Symbol map: year $1, month $2, day $3, then the word "at", hour $4, minute $5, second $6, AM or PM $7. Result: YYYY-MM-DD-HHMM.png.
Folders by date
Rename cannot create directories, so sometimes you need a loop:
for f in 2024*.txt; do
dir=$(echo "$f" | sed 's/^\([0-9-]*\)_.*/\1/')
mkdir -p "$dir"
mv "$f" "$dir/"
done
Weird characters
Use \Q...\E to quote metacharacters:
rename 's/\Q[1]\E/_v1/' *
# file[1].txt becomes file_v1.txt
\Q and \E turn off regex interpretation between them.
Chain the simple steps
Show me.
rename 'y/A-Z/a-z/' *
rename 's/ /_/g' *
rename 's/[^a-z0-9_.-]//gi' *
rename 's/_+/_/g' *
Normalize case. Replace spaces. Strip specials. Collapse underscores. Each step is simple. Together they sanitize filenames.
When the pattern does not fire
Test the regex on sample text first (Perl, or regex101.com). Use -n. Add -v for verbose. Simplify: match just part of the filename first. Build up gradually, adding capture groups one at a time.