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

All -

I'm scratching my head on this one. My windows machine has two versions of perl installed. Version 5.005_02 is the default version used from the command line. (This is a corporate administered machine, so that is why we are on an ancient version).

I also have Matlab installed, which comes bundled with version v5.8.8.

I have written a simple script to rename directories (which we need to do quite often to workaround some configuration management issues). The script works fine when using the older version of perl, but when I use v5.8.8, the rename fails with permission denied errors.

Any thoughts on this? I don't see why permission would be denied when using one version of perl over the other. Also, I have no problems renaming these directories manually.

error message:
Could not rename "D:\data\jhopkin\My Documents\Synergy\ccm_wa\ams_test +\MatlabSynergy2-jhopkin\MatlabSynergy2\3atsym3testdir" to "D:\data\jh +opkin\My Documents\Synergy\ccm_wa\ams_test\MatlabSynergy2-jhopkin\Mat +labSynergy2\@testdir": Permission denied at renameDirs.pl line 43.
code:
if(@ARGV != 3) { displayHelp(); die "Must have three arguments!"; } $from = $ARGV[0]; $to = $ARGV[1]; my $startDir = $ARGV[2]; if(!is_dir($startDir)) { die "$startDir is not a directory!"; } renameRecurse($startDir); sub renameRecurse { my $dirIn = $_[0]; my $file2 = ""; opendir DIR, $dirIn; my @entries = readdir(DIR); close DIR; foreach $file (@entries) { #skip the "." and ".." if($file ne "." && $file ne "..") { if(is_dir("$dirIn\\$file")) { renameRecurse("$dirIn\\$file"); } if(substr($file,0,length($from)) eq $from) { $file2 = $file; $file2 =~ s/$from/$to/; rename "$dirIn\\$file","$dirIn\\$file2" or die "Could +not rename \"$dirIn\\$file\" to \"$dirIn\\$file2\": $!"; } } } } sub is_dir() { if ( -d $_[0] ) { return 1; } else { return 0; } } sub displayHelp { print <<END; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ $0 Usage: $0 <from> <to> <startdirectory> Will recursively look through startdirectory for directories beginning with <from>. Will rename directory, replacing <from> with <to>. Example: $0 "@" "3ATSYM3" "C:\\change\\my\\at\\symbols" Created by Jesse Hopkins May 13th, 2010 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~ END }

Replies are listed 'Best First'.
Re: problem using rename
by toolic (Bishop) on Jul 13, 2010 at 15:10 UTC
    I don't have an explanation for the discrepancy, but here are some tips to get more debug information. Before your rename, check if both files already exist using -e; you expect the 1st file to exist, but the 2nd not to exist, right? For each file that exists, inspect the info returned by stat. Do the same for the directories to get permission info.

      Hello, I did implement your suggestions, and the permissions from stat came back 0x0777, which I'm pretty sure means I should have proper permission to rename.

      I did find something interesting using the debugger. I put a breakpoint in just before the rename occurs, and using unlocker (http://ccollomb.free.fr/unlocker/) I can see what processes have a lock on the directory. When using the older version of perl, no lock showed up when at the breakpoint. However, using the newer version, "perl.exe" shows up as having a lock on that directory.

      Turns out I need to use "closedir" rather than "close" when obtaining the directory listing above. Problem solved.

        ++ for tracking this problem down and for reporting your findings here. Perhaps it will help someone out in the future.

        By the way, I went back and analyzed your code using perlcritic, hoping that it would detect and report an opendir without a matching closedir. No luck -- at least not with its default settings. Perhaps it can be configured to catch this type of situation.

        I believe this problem can be averted using the File::Slurp module from CPAN. Its read_dir function automatically opens and closes a directory. Furthermore, it checks if the open was successful (which your code does not do). Finally, it excludes . and .. by default.

        use File::Slurp qw(read_dir); my @entries = read_dir($InDir);

        Also, the rename doc mentions that File::Copy::move might be a more portable alternative.