6

I want to be able to limit Perl's File::Find to a directory depth (below the specified search) to the specified directory and 1 & 2 subdirectories beneath it.

I want to be able to enumerate the files at the same time, if this is possible.

It must work with absolute paths.

unixman83
  • 9,421
  • 10
  • 68
  • 102

2 Answers2

7

This perlmonks node explains how to implement mindepth and maxdepth from GNU's find.

Basically, they count the number of slashes in a directory, and use that to determine the depth. The preprocess function will then only return the values where the depth is smaller than the max_depth.

my ($min_depth, $max_depth) = (2,3);

find( {
    preprocess => \&preprocess,
    wanted => \&wanted,
}, @dirs);

sub preprocess {
    my $depth = $File::Find::dir =~ tr[/][];
    return @_ if $depth < $max_depth;
    return grep { not -d } @_ if $depth == $max_depth;
    return;
}

sub wanted {
    my $depth = $File::Find::dir =~ tr[/][];
    return if $depth < $min_depth;
    print;
}

Tailored to your case:

use File::Find;
my $max_depth = 2;

find( {
    preprocess => \&preprocess,
    wanted => \&wanted,
}, '.');

sub preprocess {
    my $depth = $File::Find::dir =~ tr[/][];
    return @_ if $depth < $max_depth;
    return grep { not -d } @_ if $depth == $max_depth;
    return;
}

sub wanted {
    print $_ . "\n" if -f; #Only files
}
Konerak
  • 39,272
  • 12
  • 98
  • 118
  • How do I list all the **files** in a subdirectory using this method? This only shows me directories. – unixman83 Mar 29 '12 at 06:23
  • Actually, that line just filters out the directories from your maximum level, so they don't get processed any further. But it might make sense to comment it out to get the directories in the last level too. – Konerak Mar 29 '12 at 06:56
  • Since it uses the number of `/` to count the depth, yes. It might be better to count the difference between number of `/` of the starting path, with the difference between number of `/` of the processed. – Konerak Mar 29 '12 at 08:10
4

Here is another solution that determines the current depth within File::Find::find by counting the number of directories returned by File::Spec->splitdir, which should be more portable than counting slashes:

use strict;
use warnings;

use File::Find;

# maximum depth to continue search
my $maxDepth = 2;

# start with the absolute path
my $findRoot = Cwd::realpath($ARGV[0] || ".");

# determine the depth of our beginning directory
my $begDepth = 1 + grep { length } File::Spec->splitdir($findRoot);

find (
  {
    preprocess => sub
      { @_ if (scalar File::Spec->splitdir($File::Find::dir) - $begDepth) <= $maxDepth },
    wanted => sub
      { printf "%s$/", File::Spec->catfile($File::Find::dir, $_) if -f },
  },
  $findRoot
);
ardnew
  • 2,028
  • 20
  • 29