Scrat has asked for the wisdom of the Perl Monks concerning the following question:

Hi everyone

I'm new to Perl and find the learning process both exciting and challenging. I'd like some feedback on the following please:

#!/usr/bin/perl use strict; use warnings; my $dir = "C:\\Tickets"; if (chdir "$dir") { opendir (DIR, $dir); my @fileList = readdir DIR; foreach my $oldname (@fileList) { next if -d $oldname; my $newname = $oldname; $newname =~ s/Closed/Open/; rename $oldname, $newname; } } else { print "Error - Please check that $dir exists and is accessible.\n" +; } closedir (DIR);

It renames all files in $dir containing the word "Closed" in the filename to "Open". Is there a shorter / better way of accomplishing this task? Any advice or suggestions are welcome.

Replies are listed 'Best First'.
Re: Shorter/Better way to rename list of files in a directory
by Zaxo (Archbishop) on Jun 02, 2006 at 07:28 UTC

    You can reduce the amount of work by restricting the file list to those which actually contain "Closed". Do that by replacing opendir . . . readdir . . . closedir with glob. You can also shorten your loop code a little by taking advantage of $_ and its properties.

    #!/usr/bin/perl use strict; use warnings; my $dir = 'C:\Tickets'; chdir $dir or die "Error - Please check that $dir exists and is accessible.\n +"; my @fileList = glob '*Closed*'; foreach (@fileList) { next if -d; my $oldname = $_; s/Closed/Open/; rename $oldname, $_; }

    I also removed the big if..else by just dieing with the error message if chdir fails. That would avoid closedir on a bad handle in your code. Moving closedir inside the affirmative branch would do that, too.

    Update: You can remove the dependence on chdir entirely by including $dir in the glob pattern. With the error handling for rename added, the new version looks like this,

    #!/usr/bin/perl use strict; use warnings; my $dir = 'C:\Tickets\'; my @fileList = glob "${dir}*Closed*"; foreach (@fileList) { next if -d; my $oldname = $_; s/Closed/Open/; rename $oldname, $_ or $_ = $oldname, warn $_, ' not renamed: ', $!; }

    After Compline,
    Zaxo

        Joost++ on checking. That was careless of me. In that case, though, I'd probably just warn so the loop can keep on trying. While I'm at it, why not change $_ back so I still have an accurate file list after I'm done?

        rename $oldname, $_ or $_ = $oldname, warn "Can't rename $oldname to $_: $!";

        After Compline,
        Zaxo

        Thanks Joost

        I've been told by some developers to try and not use die in my code if possible. Reasons being that it is not "pretty" and it might complicate debugging sometimes (if not used properly).

        I think one good reason for using it though, like Zaxo stated, is to avoid further bad handles.

      Thanks Zaxo.

      I'm taking notes :-)

Re: Shorter/Better way to rename list of files in a directory
by perl_lover (Chaplain) on Jun 02, 2006 at 08:22 UTC
    use File::Rename - Perl extension for renaming multiple files
    use File::Rename qw(rename); my @list = `dir`; rename @list, sub { s/Closed/Open/ }, 1;

      I dodidn't think my @list = `dir`; does what you think it does. Backticks will put the entire listing into a single string, $list[0]. File::Rename's overridden rename will fail since the listing will never match a file name (even with only one file - newline tacked on).

      Here, too, glob would be preferable for populating @list.

      Update: Oops. Major thinko. I'll stand by preferring glob to shelling out.

      After Compline,
      Zaxo

        Erm, backticks in a list context split the returned output on $/. See perlop.

Re: Shorter/Better way to rename list of files in a directory
by Xhings (Acolyte) on Feb 08, 2010 at 12:14 UTC

    Hello Everyone,

    I am trying a similar code in my windows environment, the files are not getting renamed.

    $var = 0; $UserInputDir = "<mydir>"; opendir DH, $UserInputDir or die "Cannot open $UserInputDir: $!"; @files = readdir DH; foreach $file (@files) { next if $file=~/^\./; $OldFileName = $file; $file =~ s/.*.Log/ObjMgr_enu_$var.Log/; $var = $var + 1; rename ($OldFileName,$file); } closedir DH;

    Not sure if something needs to be changed for windows.

    Thanks Xhings

      This code i got from you guys at this forum but it gives me these errors. " Global symbol "@fileList" requires explicit package name at ./RenamingFiles.pl line 16. Execution of ./RenamingFiles.pl aborted due to compilation errors." so what is the wrong with the Array @fileList

      #!/usr/bin/perl use strict; use warnings; use IO::File; my $dir="/Users/user/Desktop/Internet Programing Course/intern +et_programming/73/"; #chdir $dir or die "Error \n"; my @filesList= glob "${dir}*Closed*"; foreach (@fileList){ next if -d; my $oldname= $_; s/Closed/Open/; rename $oldname, $_; }
        ... And rightfully so. You misspelled filesList (or fileList). You tell Perl you want to use a variable named @filesList in
        my @filesList = ...
        But then use a different, undeclared name. Because you are using the "strict" pragma, Perl knows that you want to be notified of such spelling errors.