Perl:如何停止File :: Find递归进入目录?

时间:2012-09-08 20:04:34

标签: perl

我正在查看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 个答案:

答案 0 :(得分:15)

$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),所以我把它留在了。

答案 1 :(得分:12)

虽然我认为TLP建议使用globopendir最适合您的情况,但另一种选择是使用File::Find::Rule - Find::File的界面--with maxdepth(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

答案 2 :(得分:4)

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

之下

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

删除初始./的最佳方法是使用File::Spec中的rel2abs。请注意,启用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";
}

答案 3 :(得分:0)

使用预处理“想要的”

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的元素