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

Hello all, I'm trying to lc all files and subdirectorys w/n a path. This so far
opendir(DIR, $path); @files = readdir(DIR); closedir(DIR); $ct = 0; foreach $fname (@files) { if($fname eq ".") { print "Skipped \"$fname\"\n"; next; } # skip t +he current dir if($fname eq "..") { print "Skipped \"$fname\"\n"; next; } # skip +the parent dir if(`-d $fname`) # dir find { print "DIRECTORY: $fname\n"; next; } my $newfname = lc($fname); # if(rename($fname, $newfname)) # { print "Changed $fname to $newfname\n"; } print "FILE[$ct]: \"$fname\" \"$newfname\"\n"; $ct++; }
Output:
Skipped "." Skipped ".." FILE[0]: "AUTORUN.INF" "autorun.inf" FILE[1]: "COPYRGHT.HTM" "copyrght.htm" FILE[2]: "CREDITS.HTM" "credits.htm" FILE[3]: "INDEX.HTM" "index.htm" FILE[4]: "LOADING.HTM" "loading.htm" FILE[5]: "NETRESULTS.BAT" "netresults.bat" FILE[6]: "Network Trash Folder" "network trash folder" FILE[7]: "RUN_ME.SH" "run_me.sh" FILE[8]: "SE32.EXE" "se32.exe" FILE[9]: "index.html" "index.html" FILE[10]: "private" "private" FILE[11]: "smbusers" "smbusers" FILE[12]: "GIFS" "gifs"
Notes: Plus i want to rename the current and every dir/file w/n... It's better than renaming all 3200 files...

Enjoy

Edited by japhy -- please reserve <code>...</code> tags for actual code

Replies are listed 'Best First'.
Re: Renaming Sub Dir/Files.
by jryan (Vicar) on Dec 17, 2001 at 08:58 UTC

    A few things:

    First off, use the File::Find module to recursively plow through directories. It is MUCH easier to work with than rolling your own script, and comes bundled with every version of perl.

    Secondly, use strict and -w. They will help you write clean code, catch typos, and make your life easier.

    Thirdly, on to your problem. I've rewritten your code to this: (using File::Find, strict, and -w)

    #!perl -w use strict; use File::Find; my $path = $ARGV[0]; my $ct = 0; find (\&wanted, $path); print "Number of files changed: ", $ct, "\n"; sub wanted { if ($_ ne lc($_)) { my $newname = lc($_); rename ($File::Find::name, $File::Find::dir . $newname); print $_, " was changed to ", $newname, "\n"; $ct++; } }

    If you have any questions about the above code, please reply so that we can clear up any everything.

Re: Renaming Sub Dir/Files.
by ehdonhon (Curate) on Dec 17, 2001 at 08:49 UTC

    I believe you are right about the -d $fname thing, try this:

    if ( -d "$path/$fname" ) {

    Also, on a nit-picky note, if you have really big directories, it is more efficient to do a readdir one at a time then to slurp it all into an array

    opendir(DIR, $path); @files = readdir(DIR); $ct = 0; while ($fname = readdir(DIR)) { ... # Damian Conway's 'yada, yada, yada' operator :) } closedir(DIR);

    Addendum: The same reason that your -d test is not working applies to your renaming problem. As you can see from your output, readdir doesn't return the path, only the filename. You'll probably need to include full paths with your rename() args.

      ++ehdonhon !!

      When using readdir(), it is necessary to prefix each item read with the $path passed to opendir().

      Also, notice the backticks (backquotes?) in:

      if(`-d $fname`)

      which (if not a typo in JoeCool's original code) effectively send the -d $fname as a command to the shell. Probably not what he intended.

      dmm

      You can give a man a fish and feed him for a day ...
      Or, you can
      teach him to fish and feed him for a lifetime
Re: Renaming Sub Dir/Files.
by rob_au (Abbot) on Dec 17, 2001 at 15:11 UTC
    Quick question ... What was wrong with the answers provided to you when you posted a few days ago here? All three replies, including my own, addressed your issue - with the exception of the aspect of renaming directories as well. However this could be easily implemented in the code that I provided that used File::Find through the removal of the return unless (-f _); line. eg.

    #!d:/perl/bin/perl -w use File::Find; use strict; find ({ 'wanted' => \&renamefile, }, '/path/to/sub/directory'); sub renamefile { my ($dev, $ino, $mode, $nlink, $uid, $gid); return unless (($dev, $ino, $mode, $nlink, $uid, $gid) = lstat($_) +); rename $_, lc($_); }

     

    perl -e 's&&rob@cowsnet.com.au&&&split/[@.]/&&s&.com.&_&&&print'

      That should read
      rename $_, lc($_) or warn "Could not rename $_ to @{[lc($_)]}: $!\ +n";

      In case the target filename already exists in the directory (especially if case-sensitive filesystems are involved). Or warn instead of die. Or break lc($_) out into a variable to avoid the code interpolation. Whatever.

      --
      g r i n d e r
      just another bofh

      print@_{sort keys %_},$/if%_=split//,'= & *a?b:e\f/h^h!j+n,o@o;r$s-t%t#u';
        Excellent point grinder++ ... Thanks for the pick-up

         

        perl -e 's&&rob@cowsnet.com.au&&&split/[@.]/&&s&.com.&_&&&print'