Using Perl to Rename Files in a Directory

I would like to take the directory and for all email files (* .msg), delete the "RE" at the beginning. I have the following code, but the renaming is not performed.

opendir(DIR, 'emails') or die "Cannot open directory"; @files = readdir(DIR); closedir(DIR); for (@files){ next if $_ !~ m/^RE .+msg$/; $old = $_; s/RE //; rename($old, $_) or print "Error renaming: $old\n"; } 
+7
source share
4 answers

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 might also want to check the return value of chdir .

Or add 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 reading and filtering your directory with 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); 
+10
source

You seem to assume glob behavior, not readdir like behavior.

The basic readdir system call returns only the file names in the directory and will contain two entries . and .. This is ported to the readdir function in Perl to describe the mu answer a bit more.

As an alternative, it makes no sense to use readdir if you still collect all the results in an array.

 @files = glob('emails/*'); 
+5
source

As already mentioned, your script fails due to the path you expect, and the use of the script does not match.

I would suggest a more transparent use. Hardcoding is not a good idea, IMO. As I found out one fine day, when I created a script to modify some source files, with a hard-coded path, and my colleague thought it would be a nice script to borrow to change copies of it. Email Oh!

Application:

 perl script.pl "^RE " *.msg 

i.e. regex, then a list of glob files, where the path is indicated with respect to the script, for example. *.msg , emails/*.msg or even /home/pat/emails/*.msg /home/foo/*.msg . (several globes are possible)

Using absolute paths, the user will not doubt which files he will influence, and he will also reuse the script.

The code:

 use strict; use warnings; use v5.10; use File::Copy qw(move); my $rx = shift; # eg "^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 $!; } } 
+2
source

I don’t know if the regular expression matches the specified file name, but in one line this can be done with:

perl -E'for (</path/to/emails*.*>){ ($new = $_) =~ s/(^RE)(.*$)/$2/; say $_." -> ".$new}

( say ... convenient for testing, just replace it with rename $_,$new or rename($_,$new) )

  1. <*.*> read every file in the current directory
  2. ($new = $_) =~ saves the next replacement in $new and leaves $_ unchanged
  3. (^RE) save this match in $ 1 (optional) and just map the files to "RE" at the beginning
  4. (.*$) save everything to the end of the line ($) β†’ to $ 2
  5. replace match with string in $2
0
source

All Articles