【问题标题】:Perl File::Find::RulePerl 文件::查找::规则
【发布时间】:2016-06-08 14:41:30
【问题描述】:

我正在尝试使用File::Find::Rule 仅将子文件夹的名称(非递归)复制到一个数组中。我还想排除数组@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);

所需的输出

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

电流输出

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

【问题讨论】:

  • qw 引用以空格分隔的单词。去掉逗号,添加一些空格。
  • 修改了代码,但我仍将如何构建规则以排除那些
  • 老实说,File::Find::Rule 对此太过分了。我会改用readdir
  • @ikegami 你当然有权发表这种意见。

标签: perl file find rule


【解决方案1】:

你想做什么:

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);

可能的优化:

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

也就是说,您只需要以下内容:

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

所有这些都返回完整路径,因此您需要跟进

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

通常,我会使用 FFR 代替 readdir,因为使用 readdir 的时间更长、更复杂且更容易出错。但在这种情况下,它是边界。

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 _;
   }
}

【讨论】:

  • readdir 解决方案抛出错误“while (local $_ = readdir($basedir)) {while (local $_ = readdir($basedir)) {dirhandle 的错误符号
  • 已修复。我还没有测试过那个。
  • 我尝试了第三个 File::Find::Rule 解决方案。它工作正常 b 不包括目录,但它也打印基本目录本身和具有完整路径的子目录。我如何才能将子目录的名称放入数组中(检查上面发布的我想要的输出)
  • 我错过了您只要求提供文件名。更新了我的答案。 (Matt Jacob 的答案只是我的一个副本,其中删除了错误检查。使用正则表达式也比我使用的哈希效率低。)
【解决方案2】:
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);

如果要排除的目录不像您的问题中显示的那样动态,则无需在运行时构建正则表达式:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-19
    • 2011-08-29
    • 2017-01-03
    • 1970-01-01
    • 1970-01-01
    • 2013-07-07
    • 1970-01-01
    相关资源
    最近更新 更多