4

我正在查看 Perl 的File::Find模块并按以下方式进行了尝试:

#!/usr/bin/perl

use warnings;
use strict;

use File::Find;

find({wanted => \&listfiles,
        no_chdir => 1}, ".");


sub listfiles{
    print $File::Find::name,"\n";
}

现在当我运行它时,我得到以下输出:

Noob@Noob:~/tmp$ perl test.pl 
.
./test.txt
./test.pl
./test1.txt
./hello
./hello/temp.txt

现在,我在想通过设置no_chdir=>1我会让我的代码在遇到任何目录时不进入任何目录。但输出清楚地表明我的代码正在进入hello目录并列出其文件。

那么,如何更改我的代码以使其表现得像ls而不进入任何目录。另外./,我的文件/目录名称前面可以删除吗?

我正在使用 Perl 5.14。

4

4 回答 4

16

$File::Find::prune可用于避免递归到目录中。

use File::Find qw( find );

my $root = '.';
find({
   wanted   => sub { listfiles($root); },
   no_chdir => 1,
}, $root);

sub listfiles {
   my ($root) = @_;
   print "$File::Find::name\n";
   $File::Find::prune = 1  # Don't recurse.
      if $File::Find::name ne $root;
}

prune如果您愿意,可以有条件地设置。

use File::Basename qw( basename );
use File::Find     qw( find );

my %skip = map { $_ => 1 } qw( .git .svn ... );

find({
   wanted   => \&listfiles,
   no_chdir => 1,
}, '.');

sub listfiles {
   if ($skip{basename($File::Find::name)}) {
      $File::Find::prune = 1;
      return;
   }

   print "$File::Find::name\n";
}

no_chdir没有必要——它与你想要做的事情无关——但我喜欢它的作用(防止对 cwd 进行更改),所以我把它留在了里面。

于 2012-09-08T20:22:54.157 回答
12

虽然我认为 TLP 的建议使用glob或者opendir是最好的,但另一个选择是使用File::Find::Rule --an interface for Find::File --withmaxdepth(1)来停止目录递归:

use Modern::Perl;
use File::Find::Rule;

my $directory = '.';
my @files = File::Find::Rule->maxdepth( 1 )
                            ->file
                            ->name( '*.txt' )
                            ->in( $directory );
say for @files;

在这种情况下,只会*.txt将文件名传递给@files.

样本输出:

A.txt
B.txt
columns.txt
data.txt
于 2012-09-08T20:44:18.307 回答
4

最简单的方法是使用preprocess参数从正在处理的每个目录中删除所有目录。这意味着它永远不会低于指定要搜索的目录

传递给preprocess子程序的参数列表是当前目录中的节点 - 的输出readdir。返回的值是相同的列表,但会根据您希望它们的处理方式进行排序和过滤。此代码仅删除所有目录

删除首字母的最佳方法./是使用rel2absfrom File::Spec。请注意,启用no_chdir将破坏代码,因为默认情况下rel2abs将当前工作目录作为基本目录。使用no_chdir意味着显式传递基本目录参数

use strict;
use warnings;

use File::Find 'find';
use File::Spec;

find({ wanted => \&listfiles, preprocess => \&nodirs }, '.');

sub nodirs {
  grep ! -d, @_;
}

sub listfiles {
  my $filename = File::Spec->abs2rel($File::Find::name);
  print $filename, "\n";
}
于 2012-09-08T20:34:33.537 回答
0

对“想要”使用预处理和 nop

use File::Find qw( find );

my @f;

find(
{ 
    wanted => sub {},
    preprocess => sub {
        push(@f, grep !/^\.?\./,@_); # @_ is equivalent to readdir(DIR)
                                     # '.' and '..' come for free
        # find uses what you return here for processing. 
        # since nothing (an empty list) is returned, it has nothing to recurse on
     }
},
@dirs);

如果您想要完整路径,请像这样映射 grep

push(@f, map { Spec->catfile($File::Find::dir,$_); } grep !/^\.?\./,@_);

要不就

 push(@f, map { $File::Find::dir . '/' . $_); } grep !/^\.?\./,@_);

对于后者,您可能会在 Windows 上混合使用 \ 和 /,具体取决于您如何指定 @dirs 的元素

于 2016-05-01T09:03:47.933 回答