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

Hi Monks I have this code to search for specific directories, I'd like to speed up the search by getting file::find when it hits one of these directories to stop and move on to other directories searching for the pattern, under the pattern matched directory can be a whole lot of other sub directories but none matching what I'm looking for, file::find is looking at all of these hence the slow search results I'm getting.

#!/usr/bin/perl # dirpath use strict; use warnings; use File::Find; #*****************Path Variables********************** our $testpath = 'C:\\Temp\\'; #******************************************************* find(\&dir_names, $testpath); sub dir_names { return unless -d; print "$File::Find::dir/$_\n" if /[IPD]\d{8}$/; }
I'm unable to get installed other modules like file:find:prune etc

Replies are listed 'Best First'.
Re: Limiting file:find depth question
by GrandFather (Saint) on Nov 14, 2013 at 02:14 UTC

    Add:

    $File::Find::prune = @{$dirs{$_}} > 1;

    to the end of the version of dir_names I gave in my answer to your previous question:

    sub dir_names { return if ! -d || ! /[IPD]\d{8}$/; push @{$dirs{$_}}, $File::Find::name; $File::Find::prune = @{$dirs{$_}} > 1; }
    True laziness is hard work

      Thanks for replying I'll give that a go.

      I'm not looking for dupes here, just the full path to each item matching the search pattern. I just don't want it to search under anything with the matching pattern because there can be many additional directories there so its just wasting processing time searching for the pattern which doesn't exist lower then the first instance of the required pattern. Hopefully that makes sense :-) The next posters code seems to work quicker than what I first had.

Re: Limiting file:find depth question
by kschwab (Vicar) on Nov 14, 2013 at 02:40 UTC
    If I'm understanding correctly...
    #!/usr/bin/perl # dirpath use strict; use warnings; use File::Find; #*****************Path Variables********************** our $testpath = 'C:\\Temp\\'; #******************************************************* find ({ wanted => \&wanted, preprocess => \&preprocess }, $testpath); sub wanted { return unless -d; print "$File::Find::dir/$_\n" if /[IPD]\d{8}$/; } sub preprocess { if ($File::Find::dir =~ /[IPD]\d{8}$/) { return; } else { return @_; } }

      The same thing using File::Find::Rule

      #!/usr/bin/perl -- use strict; use warnings; use Data::Dump qw/ dd pp /; use Path::Tiny qw/ path /; use File::Find::Rule qw/ find /; my $thisdir = shift || path( __FILE__ )->parent; my @all = File::Find::Rule->in( "$thisdir" ); my @dirs = File::Find::Rule->directory ->name( qr/^[IPD]\d{8}$/ ) ->prune ## don't descend ->in( $thisdir ); dd( \@all, \@dirs ); dd( find( directory => name => qr/^[IPD]\d{8}$/, prune => ## don't descend in => $thisdir, ) ); __END__ $ perl ffindrule.pl . ( [ ".", "ffindrule.pl", "a", "a/echo.txt", "a/q", "a/q/echo.txt", "a/r", "a/r/D20131114", "a/r/D20131114/fa", "a/r/I20131114", "a/r/I20131114/fa", "a/r/P20131114", "a/r/P20131114/fa", "a/r/P20131114/P20131114", "b", "b/echo.txt", "b/s", "b/s/D20131114", "b/s/D20131114/away", "b/s/I20131114", "b/s/I20131114/ru", "b/s/P20131114", "b/s/P20131114/P20131114", "b/s/P20131114/run", "b/s/t", "b/s/t/o", "b/s/t/o/p", "b/s/t/o/p/D20131114", "b/s/t/o/p/I20131114", "b/s/t/o/p/P20131114", "b/t", "b/t/echo.txt", ], [ "a/r/D20131114", "a/r/I20131114", "a/r/P20131114", "b/s/D20131114", "b/s/I20131114", "b/s/P20131114", "b/s/t/o/p/D20131114", "b/s/t/o/p/I20131114", "b/s/t/o/p/P20131114", ], ) ( "a/r/D20131114", "a/r/I20131114", "a/r/P20131114", "b/s/D20131114", "b/s/I20131114", "b/s/P20131114", "b/s/t/o/p/D20131114", "b/s/t/o/p/I20131114", "b/s/t/o/p/P20131114", )

      http://p3rl.org/Data::Dump http://p3rl.org/Path::Tiny http://p3rl.org/File::Find::Rule

      Don't fully understand whats going on here just yet, but I've tried it and it works quite a bit quicker than what I had, so thankyou.

        The preprocess sub is called for each directory that File::Find descends into. It's called before the wanted sub as a sort of filter. $File::Find::dir contains the name of the current directory being processed, including any leading path...like '/foo/bar/the-current-directory'.

        @_ contains the list of files/directories in this current directory. You choose what portion of @_ you return. If you return @_...it's a no-op, things go as normal. If you return nothing, then it will not descend below this current directory, and will not return the files in the current directory either. If you return some subset of @_, it will descend into only those directories, and only pass those files to the wanted sub.

        The code I gave won't likely work exactly for what you're trying to do, as I'm not sure I fully understand what you're doing. You'll need to figure out what you want to filter out of @_. As written, it returns nothing if the pattern matches, pruning at that point, and everything if the pattern doesn't match.

Re: Limiting file:find depth question
by Anonymous Monk on Nov 14, 2013 at 03:36 UTC