Techalicious Academy / 2026-01-15-regex-therapy

(Visit our meetup for more great tutorials)

RENAME - PATTERN CAPTURE MAGIC

This is where rename gets seriously powerful. Capture groups let you rearrange, transform, and restructure filenames.

Capture Groups Explained

Parentheses in regex capture what they match:

Pattern:  (\d+)
Input:    photo_123.jpg
Capture:  $1 = "123"

You can have multiple captures:

Pattern:  (\w+)_(\d+)\.(\w+)
Input:    photo_123.jpg
Captures: $1 = "photo", $2 = "123", $3 = "jpg"

In the replacement, use $1, $2, $3, etc.

Rearranging Parts

Swap the order of filename parts:

rename 's/(\w+)_(\d+)/$2_$1/' *.jpg

Before: photo_001.jpg
After:  001_photo.jpg

This captures "photo" as $1 and "001" as $2, then puts them back in reverse order.

Restructuring Dates

Convert MMDDYYYY to YYYY-MM-DD:

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.

Padding Numbers

Turn "file1.txt" into "file001.txt":

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.

Breaking it down:

Extracting and Keeping Parts

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.

Working with Camera Files

Camera files often look like: IMG_20240115_143022.jpg (That's IMG_YYYYMMDD_HHMMSS)

Make them human-readable:

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

Non-Capturing Groups

Sometimes you need to group but not capture. Use (?:...)

rename 's/(?:IMG_|DSC_)(\d+)/$1/' *

This matches either IMG_ or DSC_ but doesn't capture it. Only the number goes into $1.

Conditional Replacement

The /e flag lets you use Perl logic:

rename 's/(\d+)/($1 < 100 ? sprintf("0%02d", $1) : $1)/e' *

This pads numbers under 100 differently. Probably overkill, but shows what's possible.

Practical Example: AI-Generated Images

Stable Diffusion often outputs files like:

00001-1234567890-masterpiece_best_quality.png

Let's clean these up:

Step 1 - See what we have (dry run):

rename -n 's/^(\d+)-(\d+)-(.+)\.png$/$1_$3.png/' *.png

Step 2 - Simplify the prompt part:

rename -n 's/_/ /g' *           # underscores to spaces
rename -n 's/\s+/_/g' *         # collapse spaces to single underscore

Step 3 - Final format:

rename 's/^(\d+)-\d+-(.+)\.png$/image_$1.png/' *.png

Practical Example: Batch Rename 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*

This is complex! Let's break it down:

Screenshot                Fixed text
(\d{4})                   Year -> $1
-(\d{2})                  Month -> $2
-(\d{2})                  Day -> $3
at                        Fixed text
(\d+)                     Hour -> $4
\.(\d+)                   Minute -> $5
\.(\d+)                   Second -> $6
(AM|PM)                   AM/PM -> $7
\.png                     Extension

Result: YYYY-MM-DD-HHMM.png

Practical Example: Organizing by Date

Move files into date folders:

# This requires mkdir + mv, not just rename
for f in 2024*.txt; do
  dir=$(echo "$f" | sed 's/^\([0-9-]*\)_.*/\1/')
  mkdir -p "$dir"
  mv "$f" "$dir/"
done

Rename can't create directories, so sometimes you need a loop.

Handling Edge Cases

What if filenames have weird characters?

Use \Q...\E to quote metacharacters:

rename 's/\Q[1]\E/_v1/' *

Before: file[1].txt
After:  file_v1.txt

\Q and \E turn off regex interpretation between them.

Chaining Operations

Complex renames are easier as multiple steps:

# Step 1: Normalize case
rename 'y/A-Z/a-z/' *

# Step 2: Replace spaces
rename 's/ /_/g' *

# Step 3: Remove special characters
rename 's/[^a-z0-9_.-]//gi' *

# Step 4: Collapse multiple underscores
rename 's/_+/_/g' *

Each step is simple. Together they completely sanitize filenames.

Debugging Complex Patterns

When patterns don't work:

1. Test the regex on sample text first (use Perl or regex101.com) 2. Use -n to see what would happen 3. Add -v for verbose output 4. Simplify - try matching just part of the filename first 5. Build up gradually, adding capture groups one at a time