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

i'm trying to write a recursive function to step through a series of directories and subdirectories, but i'm having trouble with it. now, i know everyone's going to say i should use File::Find, but i'm doing this for a commissioned job, and the employer doesnt want to use ANY modules (something about minimalizing the program ::shrug::).

anyway, the function, called traversePath, is passed a root pathname, and it's supposed to call another instance of itself for each subdirectory and pass it $npath, but when it calls the new instance, it seems to use the same $path as the instance that called it, which basically puts it into an infinite loop, rechecking the root dir over and over again...

heres the sub:

sub traversePath($path) { ##DEBUG## print "\nDEBUG:traversing path '$path'\n"; ##DEBUG## # will contain combined filesize of all files in pwd $totalSize = 0; # convert any backslashes to fwdslashes $path =~ s/\\/\//g; # open up the current dir opendir(PATH,$path); # check for trailing slash and remove if found if(substr($path,-1,1) eq "/") {chop($path)} #read files & subdirs into @subdirs @subdirs = readdir(PATH); closedir(PATH); # loop through them all foreach $subdir (@subdirs) { # construct full pathname to current subdir or file $npath = $path."/".$subdir; # not a dir, just a file, perform a function # which returns the byte size of said file if(! -d $npath) { $totalSize += writeFile($npath); } # is a subdir else { # dont mess with cwd or parent dir if($npath ne "." && $npath ne "..") { ##DEBUG## print "\nDEBUG:npath='$npath'\n"; ##DEBUG## # call new instance of self and pass as param # to other function which records total bytesize # of directory writeDir($npath, traversePath($npath) ); } } } # return bytesize of any files found in current dir return $totalSize; }
and heres the output when initially passed the path "f:/" (it falls into an infinite loop and repeats this endlessly):
DEBUG:traversing path 'f:' DEBUG:npath='f:/winnt'
does anyone see anything i'm not seeing? any help i would greatly appreciate.

Replies are listed 'Best First'.
Re: recursive path function falls into infinite loop
by Zaxo (Archbishop) on Nov 25, 2004 at 04:59 UTC

    This sub traversePath ($path) { is wrong. Perl prototypes don't do what you think they do. In fact you never introduce the argument of the function into the function. Do this instead,

    use strict; use warnings; sub traversePath { my $path = shift;

    $path and $npath are unintended globals, which may cause problems. I added strict.pm and warnings.pm to the mix to help you fix up whatever side effects those globals have.

    File::Find is part of the core perl distribution, and I think it is silly to not use it for this. You can tell the employer I said so ;-)

    After Compline,
    Zaxo

      AAH!!! thank you! ive been messing with c++ too much lately :P and yeah, i know the no module thing is silly, but it pays the bills ::shrug::
Re: recursive path function falls into infinite loop
by tachyon (Chancellor) on Nov 25, 2004 at 07:05 UTC

    First the obligarory use File::Find. Anyway here is how to do it without using recursion - it is presented as an example rather than a recommendation. Search is width first rather than depth first. It is vaguely interesting in that we add new dirs to the array we are currently looping over. This comes in handy for other things as well.

    #!/usr/bin/perl -w use strict; my $root = "c:\\cl"; my $delim = $^O =~ m/Win32/ ? "\\" : "/"; $root .= $delim unless $root =~ m!\Q$delim\E$!; # ensure we have trail +ing slash my @dirs = ($root); my @files; for my $path (@dirs){ opendir ( DIR, $path ) or next; # skip dirs we can't r +ead while (my $file = readdir DIR) { next if $file eq '.' or $file eq '..'; # skip the dot files next if -l $path.$file; # skip symbolic links if ( -d $path.$file ) { push @dirs, $path.$file.$delim; # add the dir to dir l +ist } else { push @files, $path.$file; # add the file to file + list } } closedir DIR; } do{ local $"=$/; print "Directory list\n@dirs\n\nFile List\n@files\n" +};

    cheers

    tachyon

Re: recursive path function falls into infinite loop
by diotalevi (Canon) on Nov 25, 2004 at 05:48 UTC
Re: recursive path function falls into infinite loop
by Thilosophy (Curate) on Nov 25, 2004 at 08:30 UTC
    now, i know everyone's going to say i should use File::Find, but i'm doing this for a commissioned job, and the employer doesnt want to use ANY modules (something about minimalizing the program ::shrug::).

    Okay, this is redundant, well-known and ignores your question completely (please dont mod me down...) but you should use File::Find.

    I understand people might not want to use CPAN modules, because they need to be installed, sometimes need to be compiled, and you have to trust the module author at least a little bit. But File::Find is actually included in the standard Perl distribution, does not need to be compiled or installed, and as a standard module has passed some serious quality control. Any chance this could convince your employer?

    This is like a Java programmer being asked not to use anything from java.util.* .

Re: recursive path function falls into infinite loop
by TedPride (Priest) on Nov 25, 2004 at 15:22 UTC
    I prefer to only use recursion when it's absolutely necessary (basically, almost never), so here's my version using a queue:
    use strict; use warnings; my ($dir, $file, $path); my ($script) = $0 =~ /:?([^:]+)$/; my @dirs = ':'; while ($dir = shift(@dirs)) { opendir(DIR, $dir); while ($file = readdir(DIR)) { next if $file eq $script; $path = "$dir$file"; if (-d $path) { push (@dirs, "$path:"); next; } ######### # Open each $path and process ######### } }
    This is designed to work on a Mac (see the : directory separators) and also designed to spider whatever directory the script is in (hence no $base definition, and two lines of code making sure the script itself isn't messed with).

    Just another viewpoint. This is a regular question, and probably the third time I've posted this to various threads.

Re: recursive path function falls into infinite loop
by steves (Curate) on Nov 25, 2004 at 13:28 UTC

    Your check for . and .. is using the full path, so it will never match. There's your recursion.