Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask

Recursive Directory Listings

by curtisb (Monk)
on Sep 06, 2005 at 15:28 UTC ( #489552=perlquestion: print w/ replies, xml ) Need Help??
curtisb has asked for the wisdom of the Perl Monks concerning the following question:


I'm trying to write my own directory search function. I'm using File::stat module to help determine is the file is a directory or a single file.

However, the script runs but does not look into the directories as it finds one. Can someone please let me what I'm doing wrong.

I know that there are other ways of doing this, like the finddepth function or the built in stat() function but I
want to do it this way because I'm doing to be doing some text subbing as it goes.

Any help will be appreciated

Bobby Curtis

#!D:/Programs/PERL/bin/perl.exe -w use File::stat; my $dir = "D:/"; opendir(DIR, $dir) or die $!; while(defined (my $file = readdir(DIR))) { print "$file\n"; if(-d "$file") { print "directory found....\n"; opendir(newDIR, $file) or die $!; while(defined ($file = readdir(newDIR))) { print "got new file!!!!\n"; print "$file\n"; } closedir(newDIR); } } closedir(DIR);

Comment on Recursive Directory Listings
Download Code
Replies are listed 'Best First'.
Re: Recursive Directory Listings
by Zaxo (Archbishop) on Sep 06, 2005 at 15:47 UTC

    You're not really doing this recursively. It looks like your loop will only descend one level. File::Find is the canonical way to walk a directory tree.

    If you modify this to recurse by defining a sub which calls itself on seeing a directory, be careful to not clobber the newDIR global handle. You can do that by either localizing the global handle or else using lexical handles.

    After Compline,

      I gotta complain about the idea that File::Find is the canonical way to traverse a directory. File::Find calls a subroutine of your choice on each file it traverses, but if you want to save the results from processing those files, you have to save them in a global variable, because there's no way to capture the return values from your subroutine. Not to mention there are a lot of things that are hard to do with it. Try to use File::Find just to construct a hash with the directory structure. You can do it, but you'll have to parse the full path to the file and traverse your hash structure to insert each element, using about 10 times as much code as just saying push(@{$hash{$dir}, $file). File::Find is hard to use and leads to bad code. I never use it anymore except for the simplest of tasks.
        you have to save them in a global variable, because there's no way to capture the return values from your subroutine

        That's not quite true; you can save the results in any lexical variable in scope of the sub.

        I reckon we are the only monastery ever to have a dungeon stuffed with 16,000 zombies.

        I gotta complain about the idea that File::Find is the canonical way to traverse a directory ... I use it ...

        That argument could be made in 2005 that File::Find probably is canonical ... you could also make that argument today :)

        I prefer File::Find::Rule

Re: Recursive Directory Listings
by japhy (Canon) on Sep 06, 2005 at 15:49 UTC
    First, it doesn't appear that you're actually doing anything with the File::stat module other than including it in your program. The -d file test is part of Perl; you don't need a module to use it.

    Next, you've fallen prey to the "readdir() returns unqualified names" problem. readdir() is only returning the name of the entries in the directory -- you must prepend the PATH to those names:

    my $dir = ...; opendir my $dh, $dir or die "can't read $dir: $!"; while (defined(my $name = readdir $dh)) { my $file = "$dir/$name"; # XXX <-- here if (-d $file) { ... } } closedir $dh;
    Note the line I've commented: I'm combining the directory and the name to make a filename that is meaningful.

    Lastly, your code only goes one level down. To get indefinite depth, you'll need a recursive solution, but you should consider the File::Find module which does this for you.

    Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
    How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
      To get indefinite depth, you'll need a recursive solution

      Not quite. You simply need a stack. Recursion is just one way of obtaining a stack. What follows is another:

      my @to_visit = $base_dir; while (@to_visit) { my $dir = pop(@to_visit); opendir(my $dh, $dir) or ...; my $file; while(defined($file = readdir($dh))) { next if $file eq '.'; next if $file eq '..'; # Should use File::Spec. $file = "$dir/$file"; if (-d $file) { push(@to_visit, $file); } else { ... } } }

      Substitute pop with shift if you want to process the tree breadth first.

Re: Recursive Directory Listings
by pbeckingham (Parson) on Sep 06, 2005 at 16:14 UTC

    While you should really use a module for this, I will answer the question: Your code is not recursive - it descends only one level, and a correct solution would allow for any number. Here is an example that does what you want. Note that symbolic links to directories are not followed.

    #! /usr/bin/perl use strict; use warnings; sub list { my ($dir) = @_; return unless -d $dir; my @files; if (opendir my $dh, $dir) { # Capture entries first, so we don't descend with an # open dir handle. my @list; my $file; while ($file = readdir $dh) { push @list, $file; } closedir $dh; for $file (@list) { # Unix file system considerations. next if $file eq '.' || $file eq '..'; # Swap these two lines to follow symbolic links into # directories. Handles circular links by entering an # infinite loop. push @files, "$dir/$file" if -f "$dir/$file"; push @files, list ("$dir/$file") if -d "$dir/$file"; } } return @files; } print "file=", $_, "\n" for list ("D:"); exit 0;

    pbeckingham - typist, perishable vertebrate.
Re: Recursive Directory Listings
by sh1tn (Priest) on Sep 06, 2005 at 17:04 UTC
    What's wrong with File::Find?
    use File::Find; $start_dir = shift || '.'; find( sub{ -f $_ and push @files, $File::Find::name; -d $_ and push @dirs, $File::Find::name; }, $start_dir );

Re: Recursive Directory Listings
by sk (Curate) on Sep 06, 2005 at 15:47 UTC
Re: Recursive Directory Listings
by McDarren (Abbot) on Sep 07, 2005 at 10:05 UTC

    I know you've already had several answers, but you might wish to check out this article by merlyn

    -- Darren

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://489552]
Approved by sk
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (6)
As of 2015-11-26 05:49 GMT
Find Nodes?
    Voting Booth?

    What would be the most significant thing to happen if a rope (or wire) tied the Earth and the Moon together?

    Results (696 votes), past polls