Portrait of Michael Limberger

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.