Saurabh
Saurabh

Reputation: 21

How to abort File::Find::Rule after first match?

I am using File::Find::Rule in the following piece of code to search recursively for a file within a given directory.

Perl code

my $WD = Cwd::abs_path();

my @files = File::Find::Rule->file()->relative()->name( '*.txt' )->in($WD);

for my $file ( @files ) {
    print "file: $file\n";
}

Directory structure

|--- DIR1
     |-- subdir1
             |-- *.txt
     |-- subdir2
             |-- *.txt
             |-- *.txt

Current output

DIR1/subdir1/*.txt
DIR1/subdir2/*.txt
DIR1/subdir2/*.txt

Desired output

DIR1/subdir1/*.txt

Can someone please suggest what can be done?

Upvotes: 2

Views: 177

Answers (1)

zdim
zdim

Reputation: 66899

One way is to use its exec, in order to stop matching once the path of files found changes

my @files = File::Find::Rule->file->relative->name('*.txt')
    ->exec( sub { 
        state $path = ''; 
        return 0 if $path and $path ne $_[1]; 
        $path = $_[1]; 
     }
)->in($WD); 

The subroutine in exec is invoked once the files are found, and in it the path to files is available as the second argument in @_. Once that path changes from what it had been up to that point it means that the search has found files in a different directory.

As soon as that happens we stop changing $path so all further queries on that directory fail.

This is a simple way to detect change of directories in which files are found. Check by replacing return 0 with, for example, say "Changed from $path to $_[1]";.

Tested as perl -Mstrict -MFile::Find::Rule -wE'...', and ' in code were ".

Note: you need use feature 'state'; for state (unless you are under use v5.10 or higher). In a one-liner the -E (as opposed to -e) enables all features, in the main compilation unit.


Early comments changed (or obscured) the requirement in the title to abort altogether under the stated condition. In that case die instead of return 0, inside eval

my @files;
eval { 
    @files = File::Find::Rule->file->relative->name('*>txt')
        ->exec( sub { 
            state $path = ''; 
            die "CHANGED_DIR\n" if $path and $path ne $_[1];
            $path = $_[1];
        })
        ->in($WD);
} or do { die $@ if $@ ne 'CHANGED_DIR' }

where the string for die is made up only so that we can check and re-throw if needed.

Upvotes: 2

Related Questions