Using Perl to rename files in a directory
If your ./emails
directory contains these files:
1.msg
2.msg
3.msg
then your @files
will look something like ('.', '..', '1.msg', '2.msg', '3.msg')
but your rename
wants names like 'emails/1.msg'
, 'emails/2.msg'
, etc. So you can chdir
before renaming:
chdir('emails');
for (@files) {
#...
}
You'd probably want to check the chdir
return value too.
Or add the directory names yourself:
rename('emails/' . $old, 'emails/' . $_) or print "Error renaming $old: $!\n";
# or rename("emails/$old", "emails/$_") if you like string interpolation
# or you could use map if you like map
You might want to combine your directory reading and filtering using grep
:
my @files = grep { /^RE .+msg$/ } readdir(DIR);
or even this:
opendir(DIR, 'emails') or die "Cannot open directory";
for (grep { /^RE .+msg$/ } readdir(DIR)) {
(my $new = $_) =~ s/^RE //;
rename("emails/$_", "emails/$new") or print "Error renaming $_ to $new: $!\n";
}
closedir(DIR);
You seem to be assuming glob
-like behavior rather than than readdir
-like behavior.
The underlying readdir
system call returns just the filenames within the directory, and will include two entries .
and ..
. This carries through to the readdir
function in Perl, just to give a bit more detail on mu's answer.
Alternately, there's not much point to using readdir
if you're collecting all the results in an array anyways.
@files = glob('emails/*');
As already mentioned, your script fails because of the path you expect and the script uses are not the same.
I would suggest a more transparent usage. Hardcoding a directory is not a good idea, IMO. As I learned one day when I made a script to alter some original files, with the hardcoded path, and a colleague of mine thought this would be a nice script to borrow to alter his copies. Ooops!
Usage:
perl script.pl "^RE " *.msg
i.e. regex, then a file glob list, where the path is denoted in relation to the script, e.g. *.msg
, emails/*.msg
or even /home/pat/emails/*.msg /home/foo/*.msg
. (multiple globs possible)
Using the absolute paths will leave the user with no doubt as to which files he'll be affecting, and it will also make the script reusable.
Code:
use strict;
use warnings;
use v5.10;
use File::Copy qw(move);
my $rx = shift; # e.g. "^RE "
if ($ENV{OS} =~ /^Windows/) { # Patch for Windows' lack of shell globbing
@ARGV = map glob, @ARGV;
}
for (@ARGV) {
if (/$rx/) {
my $new = s/$rx//r; # Using non-destructive substitution
say "Moving $_ to $new ...";
move($_, $new) or die $!;
}
}