Renaming files

There’s been a lot of talk about Name Mangler today, both because a new version just came out (at a temporarily reduced price) and because some guy has been comically threatening1 Many Tricks with legal action.

Name Mangler is a file renaming application. and it looks both powerful and easy to use, but I think I’ll stick to my old command line tools. For photos, I rename the generic IMG_nnnn files that come out of my camera with the date and sequence number using my canonize program. For other files, I use a rename program whose fundamental logic was stolen from the classic first edition of Programming Perl.

Programming Perl

That first edition wasn’t very good at teaching you Perl, but it had some great examples, one of which was a little file renamer that allowed you to specify a Perl regex substitution pattern on the command line to specify the find and replace patterns. Starting with that base, I expanded it a bit, and it’s been serving me well for 15 years or more. Here’s the code:

perl:
 1:  #!/usr/bin/perl
 2:  
 3:  use Getopt::Std;
 4:  
 5:  $usage = <<USAGE;
 6:  rename - renames files via a Perl expression. Adapted from Larry Wall's
 7:           program in the first (pink) edition of the Camel book.
 8:  
 9:  usage: rename [options] '<perl expr>' files
10:  options:
11:      -t:   test - show how the renaming would be done, but don't do it
12:      -h:   help - print this message
13:  
14:  The Perl expression is mandatory. A variable, $n, can be used in the Perl
15:  expression to insert a sequence number into the new name. The files can
16:  be listed on the command line, piped in through STDIN, or given in a file,
17:  one per line.
18:  USAGE
19:  
20:  # Process options.
21:  getopts('th', \%opt);
22:  die $usage if ($opt{h});
23:  
24:  # Make sure there's a perl expression.
25:  ($perlexpr = shift) || die $usage;
26:  
27:  # Handle the different ways of getting the file names.
28:  chomp(@ARGV = <STDIN>) unless @ARGV;
29:  
30:  # Set up a one-based file counting variable.
31:  $n = 1;
32:  
33:  # Do the renaming or show how it would be done.
34:  foreach (@ARGV) {
35:    $old = $_;
36:    eval $perlexpr;
37:    die $@ if $@;
38:    if ($opt{t}) {
39:      print "$old => $_\n";
40:    }
41:    else {
42:      rename($old, $_) unless $old eq $_;
43:    }
44:    $n++;
45:  }

This is a pretty dangerous program. The regex you pass into it could get out of control and overwrite your files. This is why I added the -t option; I wanted to be able to test out tricky renames and see the results before going ahead with the renaming. More seriously, the eval on Line 36 allows you to pass in any destructive set of Perl statements you want (or don’t want). This is old-school give-yourself-enough-rope-to-hang-yourself Unix programming.

Although there are some clever options for passing in a list of files to be named through STDIN or as the contents of a file, I typically just pass them in as arguments on the command line via globbing.

For example, to rename a folder of files with all upper-case names, I’d use

rename 'tr/A-Z/a-z/' *

To rename all files with a .jpeg or .JPEG or .JPG extension to the more standard .jpg, I’d use

rename 's/jpe?g$/jpg/i' *

There’s a way to add a sequence number using the $n variable:

rename 's/.*\.pdf$/Drawing-$n.pdf/' *.pdf

That isn’t the best way to handle the sequence number if there are more than nine files. To get leading zeros so alphabetical and numerical order are the same, I’d use

rename 's/.*\.pdf$/sprintf("Drawing-%03d.pdf", $n)/e' *.pdf

which would give the files names like Drawing-001.pdf instead of just Drawing-1.pdf.

Every one of these examples could blow up in my face if I’m not careful, and none of them are safe for all possible combinations of the original file names. But if you’re good with regexes, it’s a pretty handy tool; if not, there’s always Name Mangler.


  1. I’m sure the threat of legal action isn’t funny to Many Tricks, but it’s hard not to laugh at “incrediblebees laywers.”