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

Well, I finally figured out a way to take care of my file recursion problem with File::Find...however...

When I run the code, it goes into every directory *BUT* the one I want it to. I'm sure the solution is going to make me feel dense...but here's the code:

#### rerurseDir
# Description:
#       This implements the logical recursion that we use to
#       cheat the order find() imposes;
# Params: 
#       $initPath -> The path to start in.
####
sub recurseDir
{
  my $initPath = shift;
  my @symlinks;  
  my @dirs  =  File::PathConvert::rel2abs($initPath);
  my @tempLink; 
  my @tempDir;
  
  while ( @symlinks or @dirs )
  {
    if(@symlinks)
    {
      my ($L, $D) = callFind(@symlinks);
      @tempLink = (@tempLink, @{$L});
      @tempDir = (@tempDir, @{$D});
    }  
    if(@dirs)
    {
      my ($L, $D) = callFind(@dirs);
      @tempLink = (@tempLink, @{$L});
      @tempDir = (@tempDir, @{$D});
    }
          
    @symlinks = @tempLink;
    @dirs = @tempDir;
        
    @tempLink = @tempDir = ();
  }
}  

#### callFind  
# Description:
#       A wrapper for find();
# Params:
#       @work -> The array of directories to call find on.
# Returns:
#       \@returnLink -> A reference to an array with paths to links in it
#       \@returnDir  -> The same but with normal dirs instead of links
####
sub callFind()
{
  my @work = @_;
  my @returnLink;
  my @returnDir;
  
  foreach my $entry (@work)   
  {
    find( sub
    {
      return if ( $_ eq '.' || $_ eq '..');
      
      #my $path = abs_path($File::Find::dir);
      if( -d $_ && !-l $_ )
      {
        $File::Find::prune = 1;
        push @returnDir, File::PathConvert::rel2abs($_) unless ( $_ eq "www" or $_ eq "perl" or $_ eq "lost+found" );
      }
      elsif( -l $_ && -d $_ && $_ =~ /^bin$/ or /^current$/)
      {
        push @returnLink, File::PathConvert::rel2abs($_)
      }
      else
      { 
        parseFile(File::PathConvert::rel2abs($_)) unless ($_ =~ /bak$/);
      }
    } , $entry );
  }
  return \@returnLink, \@returnDir;
}

Well, that's the portion that is relevant.  parseFile() is a subroutine that works (it just basic pattern matching).
parseFile also has the check to see if it is a text file before parsing.

Given this directory structure:

myprojectdir
\
 |-> bin -> bin_1.7
 |
 |-> bin_1.1
 |
 |-> bin_1.2
 :
 |-> bin_1.7

The above code evaluates bin_1.1 thru bin_1.6 (hidden by the ..) but IGNORES bin_1.7.

The desired outcome is exactly the opposite: evaluate bin_1.7, but ignore the rest.

Help?

Thanks,
Sam

  • Comment on It does exactly the opposite of what I want it to!

Replies are listed 'Best First'.
Re: It does exactly the opposite of what I want it to!
by merlyn (Sage) on Sep 22, 2000 at 20:57 UTC
    # This implements the logical recursion that we use to # cheat the order find() imposes;
    Perhaps you don't realize that you can control the order in which File::Find descends, at least in 5.6:
    `preprocess' The value should be a code reference. This code reference is us +ed to preprocess a directory; it is called after readdir() but before + the loop that calls the wanted() function. It is called with a list + of strings and is expected to return a list of strings. The code c +an be used to sort the strings alphabetically, numerically, or to fil +ter out directory entries based on their name alone.
    So, for example, if you wanted all subdirectories to be processed after files, you could add:
    ... preprocess => sub { sort { -d $a <=> -d $b } @_ }, ...
    to the argument list (see the docs). I haven't played with this, because I'm still on 5.5.3 waiting for 5.6.1, but give it a whirl if you're already in the "new" zone.

    -- Randal L. Schwartz, Perl hacker

      In fact, it appears that you can use 5.6's File::Find "out of the box", since on re-reading your code, you appear to just want to descend into both symlinks and "real" directories, in that order, and 5.6's find() can be told to chase links (reliably, I might add, without a chance for looping).

      -- Randal L. Schwartz, Perl hacker

        First off, I'm in 5.5.3 (5.005_03)...so I can't tweak it.

        However, what I want to do is, given the directory structure I posted above I want to follow the following logic:

        1. Parse any perlish files (determined by the shabang line...but this works) in the directory
        2. If there is a symlink to a directory, and the symlink is named "bin" or "current" go look at it, and don't look at any of the other directories.
        3. failing that, look in all the directories. (start at #1 for each, etc, recursing down).

        At the moment what it does is give me all the directories I don't want, and doesn't give me the one I do. I need a strategically placed !, but I can't quite figure out where to put it.

Re: It does exactly the opposite of what I want it to!
by isotope (Deacon) on Sep 22, 2000 at 19:57 UTC
    I don't know if this has anything to do with it, but I highly doubt that return \@returnLink, \@returnDir; is going to accomplish much, since the referenced arrays are local to the subroutine that's returning. I believe you're just going to be left with dangling pointers.

    Update: Okay, evidently Perl actually allows this (preserving data structures until all references to them are gone), but it still rubs me as bad style. That's what I get for learning C.

    --isotope