9.9. Renaming Files

Problem

You have a lot of files whose names you want to change.

Solution

Use a foreach loop and the rename function:

foreach $file (@NAMES) {
    my $newname = $file;
    # change $newname
    rename($file, $newname) or  
        warn "Couldn't rename $file to $newname: $!\n";
}

Discussion

This is straightforward. rename takes two arguments. The first is the filename to change, and the second is its new name. Perl's rename is a front end to the operating system's rename system call, which typically won't let you rename files across filesystem boundaries.

A small change turns this into a generic rename script, such as the one by Larry Wall shown in Example 9.5 .

Example 9.5: rename

#!/usr/bin/perl -w
# rename - Larry's filename fixer
$op = shift or die "Usage: rename expr [files]\n";
chomp(@ARGV = <STDIN>) unless @ARGV;
for (@ARGV) {
    $was = $_;
    eval $op;
    die $@ if $@;
    rename($was,$_) unless $was eq $_;
}

This script's first argument is Perl code that alters the filename (stored in $_ ) to reflect how you want the file renamed. It can do this because it uses an eval to do the hard work. It also skips rename calls when the filename is untouched. This lets you simply use wildcards like rename EXPR * instead of making long lists of filenames.

Here are five examples of calling the rename program from your shell:

% rename 's/\.orig$//'  *.orig
% rename 'tr/A-Z/a-z/ unless /^Make/'  *
% rename '$_ .= ".bad"'  *.f
% rename 'print "$_: "; s/foo/bar/ if <STDIN> =~ /^y/i'  *
% find /tmp -name '*~' -print | rename 's/^(.+)~$/.#$1/'

The first shell command removes a trailing ".orig" from each filename.

The second converts uppercase to lowercase. Because a translation is used rather than the lc function, this conversion won't be locale-aware. To fix that, you'd have to write:

% rename 'use locale; $_ = lc($_) unless /^Make/' *

The third appends ".bad" to each Fortran file ending in ".f" , something a lot of us have wanted to do for a long time.

The fourth prompts the user for the change. Each file's name is printed to standard output and a response is read from standard input. If the user types something starting with a "y" or "Y" , any "foo" in the filename is changed to "bar" .

The fifth uses find to locate files in /tmp that end with a tilde. It renames these so that instead of ending with a tilde, they start with a dot and a pound sign. In effect, this switches between two common conventions for backup files.

The rename script exemplifies the powerful Unix tool-and-filter philosophy. Even though we could have created a dedicated command to do the lowercase conversion, it's nearly as easy to write a flexible, reusable tool by embedding an eval . By allowing the filenames to be read from standard input, we don't have to build in the recursive directory walk. Instead, we just use find , which performs this function well. There's no reason to recreate the wheel, although using File::Find we could have.

See Also

The rename function in perlfunc (1) and in Chapter 3 of Programming Perl ; your system's mv (1) and rename (2) manpages; the documentation for the standard File::Find module (also in Chapter 7 of Programming Perl )


Previous: 9.8. Removing a Directory and Its Contents Perl Cookbook Next: 9.10. Splitting a Filename into Its Component Parts
9.8. Removing a Directory and Its Contents Book Index 9.10. Splitting a Filename into Its Component Parts