Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

recursive sub

by Anonymous Monk
on Oct 27, 2001 at 07:33 UTC ( [id://121726]=perlquestion: print w/replies, xml ) Need Help??

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

Can someone point me to some knowledge on recursive function? I'm writing my first perl program and want to walk the directory tree, processing the files in each directory. Problem is, because all of the variables are global, it doensn't unwind... (that's how I finally figure out all the variables are global!) Thanks!

Replies are listed 'Best First'.
Re: recursive sub
by Zaxo (Archbishop) on Oct 27, 2001 at 08:13 UTC

    To walk a directory tree, use File::Find;.

    I don't think you really want recursive functions for your problem, but there is a general page of definitions here. Recursive subs are interesting, but tend to be inefficient in perl. The canonical example is the factorial function in terms of itself:

    sub factorial { use integer; die "Math error: $!" if $_[0] < 0; $_[0] ? $_[0] * factorial( $_[0] - 1 ) : 1 ; }
    It piles up intermediate results on the stack.

    Update: Fixed silly error in the factorial function, thanks to blakem for the spot. Also made it more robust to bad input.

    After Compline,
    Zaxo

Re: recursive sub
by tachyon (Chancellor) on Oct 27, 2001 at 16:17 UTC

    First the obligarory use File::Find. 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 interesting in that we add new dirs to the array we are currently looping over. This comes in handy for other things as well.

    If you do a Super Search for 'walk the directory tree' and 'recursion' you will find lots of examples. You could also do a search for File::Find and find most of them :-)

    #!/usr/bin/perl -w use strict; my $root = 'c:/cluster1/'; my $delim = '/'; 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) { # skip the dot files next if $file eq '.' or $file eq '..'; # skip symbolic links next if -l $path.$file; if ( -d $path.$file ) { # add the dir to our dir list push @dirs, $path.$file.$delim; } else { # add the file to file list push @files, $path.$file; } } closedir DIR; } print "Directory list\n\n"; print "$_\n" for sort @dirs; print "\n\nFile List\n\n"; print "$_\n" for sort @files;

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: recursive sub
by loc (Beadle) on Oct 27, 2001 at 08:22 UTC
    Well, there are a few different ways to go about this. You could possibily use File::Find to accomplish that. If not, here is a sub that should work too.
    #!/usr/bin/perl -w use Cwd; use strict; sub ScanTree { my $startdir = shift; # grab startdir as param passed to sub my $initdir = &cwd; # grab current working directory (see below +) chdir( $startdir ) or die "Unable to enter $startdir: $!\n"; opendir( DIR, "." ) or die "Unable to open $startdir: $!\n"; my @names = readdir( DIR ) or die "Unable to read $startdir: $!\n" +; close( DIR ); foreach my $name( @names ) { next if( $name eq "." ); next if( $name eq ".." ); if( -d $name ) { &ScanTree( $name ); next; } } # return user to saved current working directory (see above) chdir( $initdir ) or die "Unable to enter $initdir: $!\n"; } &ScanTree( "." );

    Hope this is what you were looking for.

    -loc

      Have you considered what potentially happens to your sub if you strike a symbolic link-link-link-link-link-link? You need a -l test in there to avoid following sym links or you will quite probably end up going infinite. Also while -w is generally fine warnings get triggered if you have a recursive sub that recurses more than 100 levels deep if I recall correctly.

      cheers

      tachyon

      s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: recursive sub
by gt8073a (Hermit) on Oct 27, 2001 at 09:43 UTC
    Problem is, because all of the variables are global, it doensn't unwind...

    you have scope issues
    here is a quick example to guide you, notice the use of my

    #!/usr/bin/perl use strict; my $current = '/usr/local'; my $dir_start = '/home'; print "Current is '$current', starting with '$dir_start'\n\n"; recurse_directory( $dir_start ); print "\nDone.\n\n"; sub recurse_directory { ## variables my $current; my @local_list; ## get the dir we are in $current = $_[0]; ## get contents ## no "." or ".." dirs opendir( CURRENTDIR, "$current" ) or die "Can not open $current: $!"; @local_list = grep !/^\.\.?$/, readdir CURRENTDIR; closedir( CURRENTDIR ) or die "Can not close $current: $!"; ## now loop over the contents foreach my $x ( @local_list ) { my $file = $current . '/' . $x; recurse_directory( $file ) if ( $file -d ); print "$file\n"; } }
    there is quite a bit wrong with this snippet, as i am sure will be pointed out, but this should help you with your scope problems.

    the in depth differences between global, local, and my variables may be more than you are looking for, but a good place to start is page 189 of the Camel book, 2nd edition( no idea on the page in 3rd edition, sorry )

    Will perl for money
    JJ Knitis
    (901) 756-7693
    gt8073a@industrialmusic.com

      you have scope issues

      and you have symlink and dot file issues ;-) see reply above.

      cheers

      tachyon

      s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: recursive sub
by mkmcconn (Chaplain) on Oct 30, 2001 at 11:58 UTC
    Anonymous,
    The following function seems to fit your description
    # recursively operate on files use DirHandle; sub go_in { my $dir = shift; # -- this just flips the toothpicks, in case # we're in windows. It's not really necessary. $dir =~ tr/\\/\//; # -- and this just adds a trailing slash, for # concatenating later. if ($dir !~ m/\/$/){ $dir .= '/'; } my $dh = new DirHandle ($dir); # or die, I suppose. return unless $dh; while ( defined( my $item = $dh->read )) { next if $item =~ m/\.$/; $item = $dir.$item; # paranoid about symlinks. next if -l $item; # -- call recursively, but not forever: # (discard visited directories) if ( -d $item ){ go_in($item); next; } # -- operate on files here -- # } }

    DirHandle is a very simple module, suitable for simple tasks.
    updated: edited out some playful peculiarities.
    mkmcconn

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://121726]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (5)
As of 2024-04-24 08:12 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found