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

This chunk of code, using READDIR, nicely reads in the contents of the top level the directory, places it into an array then prints it back out. However, READDIR on Win32 ( and everywhere else I would assume) does NOT read into the sub-directories. Just the top level. How does one accomplish this?

#!/usr/bin/perl -w use strict; use FileHandle; my $localdir="c:\\temp"; opendir LOCALDIR, "$localdir"; my @local_files = grep !/^\.\.?$/, readdir LOCALDIR; closedir LOCALDIR; my $n=@local_files; for (my $i=0; $i<$n; $i++){ print "$local_files[$i]\n"; }

Replies are listed 'Best First'.
Re: Win32 Recursive Directory Listing
by tachyon (Chancellor) on Apr 09, 2002 at 20:53 UTC

    OK I like to know how things work so although you can and probably should do this with a module here is how you roll your own (without even needing recursion)

    #!/usr/bin/perl -w use strict; my $root = 'c:/winnt/'; my @dirs = ($root); my @files; for my $path (@dirs){ opendir ( DIR, $path ) or next; # skip dirs we can't read while (my $file = readdir DIR) { next if $file eq '.' or $file eq '..'; # skip dot files next if -l $path.$file; # skip sym links if ( -d $path.$file ) { push @dirs, $path.$file.'/'; # add dir to list } else { push @files, $path.$file; # add file to list } } closedir DIR; } print "Directory list\n\n"; print "$_\n" for sort @dirs; print "\n\nFile List\n\n"; print "$_\n" for sort @files;

    How we do it is simple. First we define our root dir and our delimiter and push this into the @dirs array. We then iterate over the @dirs array. Although @dirs initially only contains the root dir we find all its subdirs and push them onto the end of this. Thus after the first iteration @dirs contains all the subdirs of our root dir which we then search for subdirs and so on ad infinitum until we have no more subdirs. Testing for the . and .. dirs is important as these are the current and one level up dirs. We do not want to follow symbolic links otherwise we may end up in an infinite loop.

    We then test each 'file' to see if it is a direcory or a file and push it into the appropriate array. By the fime we have iterated over @dirs we have root and all it subdirectories (to whatever depth) in @dirs and all the files in @files. These are the fully qualified paths.

    Hope this helps.




Re: Win32 Recursive Directory Listing
by thelenm (Vicar) on Apr 09, 2002 at 20:03 UTC
    On Unix systems, the File::Find module is what you want. I suspect it will work under Win32 too, but I don't have enough experience on Win32 to say for sure.
Re: Win32 Recursive Directory Listing
by lucs (Sexton) on Apr 09, 2002 at 20:03 UTC
    perldoc File::Find
Re: Win32 Recursive Directory Listing
by silent11 (Vicar) on Apr 09, 2002 at 21:33 UTC
    We all know that there is ' more than one way to do it ', well can some one tell me why 'my' way doesn't work? I'm courious to know. Thanks in advance!
    use strict; my @dirs = system(dir /b /s); for (@dirs) {print;}

      The system function returns the exit status code of the program, not its output. See system and the qx or backticks operator. More importantly, your code will only run on Win32, whereas File::Find will work on Unix and related systems as well.

        There is an issue with File::Find (and hand-rolled solutions too) on win32 when working with mapped file systems. It will stat every file to determine whether it is a directory and needs to be recursed into. Each stat requires a server round-trip which can be slow.

        If you call the DOS function you can do the whole thing in one hit. If you are certain the script will never be needed on a non-win32 system there is no issue with portability.

        I was doing something similar a while back, and reduced the run time from several hours to under ten minutes.

        -- iakobski

      You might better want to use backticks or pipe-open instead of system to get the output on STDOUT...

      e.g backticks

      my @files = `dir /b /s`; chomp(@files);
      or with pipe-open:
      unless (open (DIR, "dir /b /s |")){ die "Error: couldn't execute dir-command: $!\n"; } else { my @files = <DIR>; close (DIR); chomp(@files); }

      Best regards,
      perl -le "s==*F=e=>y~\*martinF~stronat~=>s~[^\w]~~g=>chop,print"

Re: Win32 Recursive Directory Listing
by BUU (Prior) on Apr 09, 2002 at 20:04 UTC