Search code examples
perlfindfile-find

Perl File::Find::Rule excluding directories from an array


I have a set of directories in an array as

chdir /tmp;
my @dir=("test" "abc" "def")

I am looking for a way using File::Find::Rule to find all files in /tmp recursively, but excluding files from the ones in @dir.

I could get all files from @dir by

my $rule =  File::Find::Rule->new;
$rule->file;
my @files = $rule->in( @dir );

But I cannot negate the condition, that is to exclude @dir. I was trying

chdir /tmp;
@files = File::Find::Rule->new
    ->file()
    ->name(@dir)
    ->prune
    ->in( "." );

but no output.


Solution

  • The documentation lists this example:

    ignore CVS directories

    my $rule = File::Find::Rule->new; $rule->or($rule->new
                   ->directory
                   ->name('CVS')
                   ->prune
                   ->discard,
              $rule->new);
    

    Note here the use of a null rule. Null rules match anything they see, so the effect is to match (and discard) directories called 'CVS' or to match anything

    You could probalby do the same thing:

    my @exclude_dirs = qw(test abc def);
    my $rule = File::Find::Rule->new; 
    $rule->or($rule->new
                   ->directory
                   ->name(@exclude_dirs)
                   ->prune
                   ->discard,
              $rule->new);
    my @files = $rule->in('/tmp');
    

    Consider this example:

    foo@bar:~/temp/filefind> tree
    .
    ├── bar
    │   ├── bar.txt
    │   └── foobar.txt
    ├── baz.txt
    └── foo
        └── foo.txt
    
    2 directories, 4 files
    

    Here's the code:

    #!/usr/bin/perl
    use strictures;
    use File::Find::Rule;
    use Data::Dump;
    
    my @exclude_dirs = qw(foo);
    my $rule = File::Find::Rule->new; 
    $rule->or($rule->new
                   ->directory
                   ->name(@exclude_dirs)
                   ->prune
                   ->discard,
              $rule->new);
    my @files = $rule->in('filefind');
    dd \@files;
    

    And now I run this:

    foo@bar:~/temp> perl file-find.pl
    [
      "filefind",
      "filefind/baz.txt",
      "filefind/bar",
      "filefind/bar/bar.txt",
      "filefind/bar/foobar.txt",
    ]