Munge All the Filenames!
A Shameful Admission
It has come to pass many times that I've needed to rename a bunch of files. I had a bunch of strategies for doing this, and I never picked one and stuck to using it. I certainly never build a reusable tool for it, which meant that every time I did it, I screwed things up in a new and exciting way.
Sometimes a one-liner in shell would do it:
for f in *.txt; { mv "$f" "$f.old" }
Or maybe:
for f in *.txt; { BASE=$(basename $f .txt); mv "$f" "$BASE.yaml" }
Other times, I'd resort to Perl:
use autodie qw(rename);
my @files = grep { -f } <*>;
for my $file (@files) {
my $new = $file =~ s/^([0-9]+)/sprintf '%4u', $1/e;
rename $file => $new;
}
Actually, I am relieved to note that I can't even easily show what I usually did, which was just deplorable. I'd run Vim and do something like :r !find . -type f
to get a list of files, then do some :v/.../d
to delete files I didn't care to rename, then maybe put it in a __DATA__
section to iterate over or maybe use a block yank-and-put to build up a file that looked like this:
file1.pdf "File 01.pdf"
file2.pdf "File 02.pdf"
file10.pdf "File 10.pdf"
file18.pdf "File 18.pdf"
Then I could just :%s/^/mv /
to put a mv
at the front of every line and pipe it through sh
! Seriously, I did this. But I've gotten better. Now I use rename.
rename '$_ = ucfirst; s/([0-9]+)/sprintf "%02u", $1/e;' *.pdf
I love writing one-liners, but I always get them wrong. For example, that one above has a bug. With lots of one-liners, I can just run them over and over until I get it right. When renaming files, though, you don't want to actually do it until you have it right – so there's the -n
switch to do a dry run:
~$ rename -n '$_ = ucfirst; s/([0-9]+)/sprintf "%02u", $1/e;' *.pdf
rename file1.pdf File01.pdf
rename file10.pdf File10.pdf
rename file18.pdf File18.pdf
rename file2.pdf File02.pdf
Oops. I need to add whitespace.
~$ rename -n '$_ = ucfirst; s/ *([0-9]+)/sprintf " %02u", $1/e;' *.pdf
rename file1.pdf File 01.pdf
rename file10.pdf File 10.pdf
rename file18.pdf File 18.pdf
rename file2.pdf File 02.pdf
Great! Drop the -n
and our files get renamed!
If you need to do something more complicated – like keep a counter to number your files, you can write a whole subroutine in a "real" program:
use strict;
use File::Rename;
my @files = @ARGV || die "no files given!";
my $i = 1;
my $width = length scalar @files;
File::Rename::rename(
@files,
sub { s/^/sprintf '%0*u - ', $width, $i++/e },
);
See Also
mfn is a specialized renamer for dealing in bulk with terrible filenames