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 $!;
    }
}