Book HomeLearning Perl, 3rd EditionSearch this book

13.2. Renaming Files

Giving an existing file a new name is simple with the rename function:

rename "old", "new";

This is similar to the Unix mv command, taking a file named old and giving it the name new in the same directory. You can even move things around:

rename "over_there/some/place/some_file", "some_file";

This moves a file called some_file from another directory into the current directory, provided the user running the program has the appropriate permissions.[284]

[284]And the files must reside on the same filesystem. We'll see why this rule exists a little later in this chapter.

Like most functions that request something of the operating system, rename returns false if it fails, and sets $! with the operating system error, so you can (and often should) use or die (or or warn) to report this to the user.

One frequent[285] question in the Unix shell-usage newsgroups is how to rename everything that ends with ".old " to the same name with ".new". Here's how to do it in Perl nicely:

[285]This isn't just any old frequent question; the question of renaming a batch of files at once is the mostfrequent question asked in these newsgroups. And that's why it's the first question answered in the FAQs for those newsgroups. And yet, it stays in first place. Hmmm.

foreach my $file (glob "*.old") {
  my $newfile = $file;
  $newfile =~ s/\.old$/.new/;
  if (-e $newfile) {
    warn "can't rename $file to $newfile: $newfile exists\n";
  } elsif (rename $file, $newfile) {
    ## success, do nothing
  } else {
    warn "rename $file to $newfile failed: $!\n";
  }
}

The check for the existence of $newfile is needed because rename will happily rename a file right over the top of an existing file, presuming the user has permission to remove the destination filename. We put the check in so that it's less likely that we'll lose information this way. Of course, if you wanted to replace existing files like wilma.new, you wouldn't bother testing with -e first.

Those first two lines inside the loop can be combined (and often are) to simply be:

(my $newfile = $file) =~ s/\.old$/.new/;

This works to declare $newfile, copy its initial value from $file, then select $newfile to be modified by the substitution. You can read this as "transform $file to $newfile using this replacement on the right." And yes, because of precedence, those parentheses are required.

Also, some programmers seeing this substitution for the first time wonder why the backslash is needed on the left, but not on the right. The two sides aren't symmetrical: the left part of a substitution is a regular expression, and the right part is a double-quotish string. So we use the pattern /\.old$/ to mean ".old anchored at the end of the string" (anchored at the end, because we don't want to rename the first occurrance of .old in a file called betty.old.old), but on the right we can simply write .new to make the replacement.



Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.