21

I'm a bit confused from File::Find documentation... What is the equivalent to $ find my_dir -maxdepth 2 -name "*.txt"?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
David B
  • 29,258
  • 50
  • 133
  • 186
  • `my @files = \`find $my_dir -maxdepth 2 -name *.txt\`;`... I don't get the `Wanted` sub. Can't I just give a regex? – David B Sep 25 '10 at 21:07
  • Another question about [Finding files with Perl](http://stackoverflow.com/questions/17754931/finding-files-with-perl) mentions some alternatives not mentioned in the answers below. – G. Cito Jan 05 '15 at 15:00

4 Answers4

32

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"
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
justintime
  • 3,601
  • 4
  • 22
  • 37
  • +1 I think this is the simplest, most `$ find` -like solution suggested. – David B Sep 28 '10 at 15:17
  • unfortunately with iterator, it fetches all directory structure into memory. when structure is so big (typical), then process will consume a lot of memory. Its why this resolution is often not good. In opposite, scannind dir tree by system find command gives option for fetching it online without consuming big amount of RAM. – Znik Feb 02 '18 at 13:46
7

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 (removed in v5.20 but on CPAN):

% 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.

brian d foy
  • 129,424
  • 31
  • 207
  • 592
6
use File::Find ; 
use Cwd ; 

my $currentWorkingDir = getcwd;

my @filesToRun = ();
my $filePattern = '*.cmd' ; 
#add only files of type filePattern recursively from the $currentWorkingDir
find( sub { push @filesToRun, $File::Find::name  
                                    if ( m/^(.*)$filePattern$/ ) }, $currentWorkingDir) ;

foreach  my $file ( @filesToRun  ) 
{
    print "$file\n" ;   
} 
brian d foy
  • 129,424
  • 31
  • 207
  • 592
Yordan Georgiev
  • 5,114
  • 1
  • 56
  • 53
2

There's also the handy find2perl utility. Use it instead of the Unix find command, with the same command-line arguments as 'find', and it will generate the corresponding Perl code that makes use of File::Find.

$ find2perl my_dir -maxdepth 2 -name "*.txt"
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
mlbright
  • 2,181
  • 2
  • 17
  • 14