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

Whilst experimenting perlishly with recursion , and reading many of the posts here at PM that cropup with recursion and File Find quite regularly, I have the following code that decends a directory tree, building a hash as it goes. Keys that are filenames hold a url as their value, Keys that are directories store a hashref of that directory's files &&|| directories.

I would like to determine if File::Find can be used for this, but I cannot figure out how to do this with the \&wanted sub.

#!/usr/bin/perl -w -T use strict; use Data::Dumper; my $urlbase='http://proxy/cgi-bin/getobj.pl?'; my $path = shift @ARGV or die; sub recurse { my $dir = shift; if ( '/' ne substr($dir,-1,1) ) { $dir = $dir.'/' }; my $base = substr($dir , length($path)); my %tree; opendir(DR , $dir); my @stuff=(readdir DR); closedir(DR); LOOP: foreach my $path ( @stuff ) { my $dot = substr ($path,1,1); next LOOP if ( $dot eq "" || $dot eq "." ); if (-f "$dir$path") { # Associate a file with a url $tree{$path}="$urlbase$base$path"; } if (-d "$dir$path" && ! -l "$dir$path") { # Decend into directory (ere's the recursion. my %glob = recurse( "$dir$path" ); $tree{$path}=\%glob; } } return %tree; } print Dumper recurse ( $path ) ; exit;

WTF am I using the hash for? ybiC already has suspicions, and this confirms them.



I cant believe its a syntax error

Replies are listed 'Best First'.
Re: Everybody loves not using File::Find
by pg (Canon) on Nov 27, 2002 at 03:45 UTC
    Here comes the version with File::Find, although "Everybody loves not using File::Find" :-) (I only tested it on Win. At home, no UNIX around. I expect some small modifications to satisfy UNIX, such like symlinks etc.)
    use File::Find; use Data::Dumper; my $files = {}; find(sub {wanted($files);}, "c:/perl58"); print Dumper($files); sub wanted { if (($_ ne ".") && ($_ ne "..")) { insert(shift); } } sub insert { my @elements = split("/", $File::Find::name); my $parent = shift; my $index; foreach ($index = 0; $index <= $#elements; $index ++) { if (($index == $#elements) && (-f $_)) { $parent->{$elements[$index]} = $File::Find::name; } else { if (!defined($parent->{$elements[$index]})) { $parent->{$elements[$index]} = {}; } $parent = $parent->{$elements[$index]}; } } }

      ++pg for headspin value ! This code does run on linux , a small mod to &wanted to make it symlink safe(r)

      if (($_ ne ".") && ($_ ne "..") && (! -l $_)) {

      However when the hash is unrolled, the first key is null, and keys are generated all the way down to the actual search base.

      eg: running this script on /usr/local/html/users/me returns VAR1='' => { 'usr'=> { 'local' => { 'html' => { 'me' => { etc,etc. Before the real search begins.

      So to accomodate for that we can change the assignment to @elements

      @elements = split("/" , substr($File::Find::name, length($ARGV[0])));
      Obviously that shift @ARGV had to be replaced with $ARGV[0] , which might be better done assigning to a (heaven forefend) global.

      'b'x2 || ! 'b'x2
Re: Everybody loves not using File::Find
by tadman (Prior) on Nov 27, 2002 at 02:40 UTC
    That "LOOP:" gave me the willies, so I wondered what it would look like tidied up:
    use warnings; use strict; use Data::Dumper; my $urlbase = 'http://proxy/cgi-bin/getobj.pl?'; sub recurse { my ($basedir, $dir) = @_; $dir = $basedir unless (defined($dir)); $dir = "$dir/" unless ($dir =~ m#/$#); my $base = substr($dir, length($basedir)); my %tree; opendir(DIR , $dir); foreach my $path (readdir(DIR)) { next if ($path =~ /^\.\.?/); # . & .. my $dirpath = "$dir$path"; if (-f $dirpath) { $tree{$path} = "$urlbase$base$path"; } elsif (-d $dirpath && !-l $dirpath) { $tree{$path} = recurse($basedir, $dirpath); } } closedir(DIR); return \%tree; } my ($path) = @ARGV; unless (defined($path)) { print "Usage: $0 path\n"; exit(0); } print Dumper recurse ($path);
      To append the '/' when required with a regex :-
      $dir=~s#(?<!/)$#/#;
      Also I would suggest :-
      next if ($path =~ /^\.\.?$/);
      So that '.filename' file/directories are not ignored if you want to map these too.

      Hope it helps
      UnderMine

        Argh! non skinny substitution delimiters :). Lemme see if I understand though.

        s#(?<!/)$#/#;

        means... if the character / is NOT occuring as the last char in the string, substitute a / for the match, BUT since this is a zero width match ?< then presumably "/" replaces "" at the end of the string?.

        Not being a regex guru, and knowing that (at least for me), you're only as good as your best reference!! , I used the Perl In a Nutshell regex reference to unwrap this. However, the only mention of ! , is as the !~ operator, or in situations like (?<!=...) or (?!...) , being hesitant to use code before I understand it, am I even close?

        evil groan I don't want to map .anything (should have been clearer in the 1st post) , I like the .invisibility - and shall not shirk it!

Re: Everybody loves not using File::Find
by John M. Dlugosz (Monsignor) on Nov 27, 2002 at 05:34 UTC
    The only time (since I learned better) that I didn't use File::Find was on ActiveState running in ASP under IIS on Windows, where it inexplicibly doesn't work. Rather than debug it and get to the bottom of it, my needs were simple enough that it was trivial to just do without.
Re: Everybody loves not using File::Find
by BrowserUk (Patriarch) on Nov 27, 2002 at 01:53 UTC

    Anyone know how to contact paco? This looks to be just what he needs.


    Okay you lot, get your wings on the left, halos on the right. It's one size fits all, and "No!", you can't have a different color.
    Pick up your cloud down the end and "Yes" if you get allocated a grey one they are a bit damp under foot, but someone has to get them.
    Get used to the wings fast cos its an 8 hour day...unless the Govenor calls for a cyclone or hurricane, in which case 16 hour shifts are mandatory.
    Just be grateful that you arrived just as the tornado season finished. Them buggers are real work.