I'm a bit confused from File::Find
documentation... what is the equivalent to $ find my_dir -maxdepth 2 -name "*.txt"
?
views:
127answers:
3You should give find
a preprocess
function, as described in this comment on perlmonks.org.
use File::Find;
my $max_depth = 2;
find( { preprocess => \&limit_depth,
wanted => \&textfiles_only
}, "my_dir");
sub limit_depth {
Count number of slashes to find out depth. Exercise for the reader: substract the number of slashes in the original path. That's zero for "my_dir"
, of course.
my $depth = $File::Find::dir =~ tr[/][];
The minus one is necessary because GNU find starts counting at one, the Perl module at zero.
if ($depth < $max_depth - 1) {
return @_;
} else {
Do not process directory children if we're at $max_depth.
return grep { not -d } @_;
}
}
This is the wanted
subroutine, which is called on each file at depth <= 2.
sub textfiles_only {
print "$File::Find::name\n" if $_ =~ /\.txt$/;
}
I think I'd just use a glob
since you really don't need all the directory traversal stuff:
my @files = glob( '*.txt */*.txt' );
I made File::Find::Closures to make it easy for you to create the callbacks that you pass to find
:
use File::Find::Closures qw( find_by_regex );
use File::Find qw( find );
my( $wanted, $reporter ) = File::Find::Closures::find_by_regex( qr/\.txt\z/ );
find( $wanted, @dirs );
my @files = $reporter->();
Normally, you can turn a find(1) command into a Perl program with find2perl
:
% find2perl my_dir -d 2 -name "*.txt"
But apparently find2perl
doesn't understand -maxdepth
, so you could leave that off:
% find2perl my_dir -name "*.txt"
#! /usr/local/perls/perl-5.13.5/bin/perl5.13.5 -w
eval 'exec /usr/local/perls/perl-5.13.5/bin/perl5.13.5 -S $0 ${1+"$@"}'
if 0; #$running_under_some_shell
use strict;
use File::Find ();
# Set the variable $File::Find::dont_use_nlink if you're using AFS,
# since AFS cheats.
# for the convenience of &wanted calls, including -eval statements:
use vars qw/*name *dir *prune/;
*name = *File::Find::name;
*dir = *File::Find::dir;
*prune = *File::Find::prune;
sub wanted;
# Traverse desired filesystems
File::Find::find({wanted => \&wanted}, 'my_dir');
exit;
sub wanted {
/^.*\.txt\z/s
&& print("$name\n");
}
Now that you have the starting programming, you can plug in whatever else you need, including a preprocess
step to prune the tree.
Personally I prefer File::Find::Rule as this doesn't need you to create callback routines.
use strict ;
use Data::Dumper ;
use File::Find::Rule ;
my $dir = shift ;
my $level = shift // 2 ;
my @files = File::Find::Rule->file()
->name( "*.txt" )
->maxdepth( $level )
->in( $dir );
print Dumper( \@files ) ;
Or alternatively create an iterator :
my $ffr_obj = File::Find::Rule->file()
->name( "*.txt" )
->maxdepth( $level )
->start ( $dir );
while (my $file = $ffr_obj->match() )
{
print "$file\n"
}