Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery

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
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,

Re: Recursive Directory Listings
by sk (Curate) on Sep 06, 2005 at 15:47 UTC
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 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 pondering the Monastery: (4)
As of 2014-10-02 01:21 GMT
Find Nodes?
    Voting Booth?

    What is your favourite meta-syntactic variable name?

    Results (41 votes), past polls