简体   繁体   中英

Perl File::Find::Rule

I am trying to copy just the subfolders' names (non-recursively) into an array using File::Find::Rule . I also want to exclude the directory names which are mentioned in array @exclude_dirs

#!/usr/bin/perl

use strict;
use warnings;
use File::Find::Rule;
use Data::Dumper;

my $basedir      = "C:\/Test";
my @exclude_dirs = qw( dir1_excl dir2_excl );

my @subdirs = File::Find::Rule
    ->directory()
  # ->name(@exclude_dirs)->prune->discard, File::Find::Rule->new 
    ->maxdepth(1)
    ->in( $basedir );

print Dumper(\@subdirs);

Desired Output

$VAR1 = [
          'dir1',
          'dir2',
          'dir3'
        ]

Current Output

$VAR1 = [
          'C:/Test',
          'C:/Test/dir1',
          'C:/Test/dir1_excl',
          'C:/Test/dir2',
          'C:/Test/dir2_excl',
          'C:/Test/dir3'
        ]

What you were trying for:

my @subdirs =
     File::Find::Rule
        ->mindepth(1)
        ->maxdepth(1)
        ->directory
        ->or(
            File::Find::Rule
                ->name(@exclude_dirs)
                ->discard
                ->prune,
            File::Find::Rule
                ->new
          )
        ->in($basedir);

Possible optimization:

my @subdirs =
     File::Find::Rule
        ->mindepth(1)
        ->maxdepth(1)
        ->or(
            File::Find::Rule
                ->name(@exclude_dirs)
                ->discard
                ->prune,
            File::Find::Rule
                ->directory
          )
        ->in($basedir);

That said, all you need is the following:

my @subdirs =
    File::Find::Rule
        ->mindepth(1)
        ->maxdepth(1)
        ->not_name(@exclude_dirs)
        ->directory
        ->in($basedir);

All of those return full paths, so you'd need to follow up with

s{^\Q$basedir\E/}{} for @subdirs;

Normally, I'd use FFR instead readdir because using readdir is far longer, far more complex and far more error-prone. But in this case, it's borderline.

my @subdirs;
{
   my %exclude_dirs = map { $_ => 1 } '.', '..', @exclude_dirs;

   opendir(my $dh, $basedir)
      or die("Can't read dir \"$basedir\": $!\n");

   while (my $fn = readdir($dh)) {
      next if $exclude_dirs{$fn};

      my $qfn = "$basedir/$fn";
      if (!stat($qfn)) {
         warn("Skipping \$qfn\": Can't stat: $!\n");
         next;
      }

      push @subdirs, $fn if -d _;
   }
}
use strict;
use warnings;

use Data::Dumper;

my $basedir      = "C:/Test";
my @exclude_dirs = qw(. .. dir1_excl dir2_excl);
my $exclude_pat  = join('|', map { quotemeta } @exclude_dirs);

opendir(my $dh, $basedir) or die $!;
my @subdirs = grep { -d "$basedir/$_" && !/^(?:$exclude_pat)\z/i } readdir($dh);
closedir($dh);

print Dumper(\@subdirs);

If the directories to exclude aren't as dynamic as they seem in your question, there's no need to build the regular expression at runtime:

my @subdirs = grep { -d "$basedir/$_" && !/^(?:\.|\.\.|dir1_excl|dir2_excl)\z/i } readdir($dh);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM