如何获取目录及其子目录中的文件句柄?

时间:2011-06-20 20:59:40

标签: perl

所以,我最近注意到在脚本中使用了opendir,并希望稍微更改它,以便它返回目录的子文件夹中的文件以及目录本身中的文件。在调查之后,我无法为opendir找到任何类型的递归选项,并且在使用glob返回标量时遇到了麻烦。因此,我认为只要问一下:在dir及其子目录中处理所有文件的标准方法是什么,而不是用其中任何一个进行捏造,我觉得更为谨慎?

4 个答案:

答案 0 :(得分:7)

经典的方法是使用File::Find,它具有成为核心模块的优势,但它可能有点痛苦。如果您能够使用第三方模块,File::Util非常方便:

use File::Util;
my $fu = File::Util->new;

my $root = 'foo/bar';

my @dirs_and_files = $fu->list_dir($root, '--recurse');
my @files_only     = $fu->list_dir($root, '--recurse', '--files-only');

答案 1 :(得分:5)

find2perl为目录树中的所有文件生成递归调用的示例代码。

> find2perl . -type f -print
#! /usr/bin/perl -w
    eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
        if 0; #$running_under_some_shell

use strict;
use File::Find ();

# Set the variable $File::Find::dont_use_nlink if you're using AFS,
# since AFS cheats.

# for the convenience of &wanted calls, including -eval statements:
use vars qw/*name *dir *prune/;
*name   = *File::Find::name;
*dir    = *File::Find::dir;
*prune  = *File::Find::prune;

sub wanted;



# Traverse desired filesystems
File::Find::find({wanted => \&wanted}, '.');
exit;


sub wanted {
    my ($dev,$ino,$mode,$nlink,$uid,$gid);

    (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&
    -f _ &&
    print("$name\n");
}

根据需要将其用作模板。

答案 2 :(得分:0)

事实上,CPAN上可能有一个关于此的模块,但我只是自己做了递归:

use File::Spec;

sub find($) {
    opendir my $dh, $_[0] or die;
    return
        map { $_, -d $_ ? find($_) : () }
        map { /\A\.\.?\z/ ? () : File::Spec->catfile($_[0], $_) } readdir $dh;
}

这包括结果中的目录 - 如果您只想 文件,请将第一个map()来电替换为map { -d $_ ? find($_) : $_ }

您需要记住的唯一事项是,到目前为止需要将路径添加到readdir()之前,并返回...,因此需要将其删除 - 第二个map()调用(首先应用)完成这两件事。如果你知道正在运行的操作系统,你可以插入/\而不是调用File::Spec->catfile(),但后者有利于移植。

答案 3 :(得分:0)

修改:我已将此基本结构上传到CPAN File::chdir::WalkDirshould be live soon),导出walkdir,类似于下面显示的内容。

引自my answer to another question

我发现使用完美合作伙伴opendir / readdirFile::chdir(我最喜欢的CPAN模块,非常适合跨平台)的递归目录行走功能可以让人轻松清晰如果需要,可以操作包含子目录的目录中的任何内容(如果没有,省略递归)。

示例(一个简单的深ls):

#!/usr/bin/env perl
use strict;
use warnings;

use File::chdir; #Provides special variable $CWD
# assign $CWD sets working directory
# can be local to a block
# evaluates/stringifies to absolute path
# other great features

walk_dir(shift);

sub do_something {
  print shift . "\n";
}

sub walk_dir {
  my $dir = shift;
  local $CWD = $dir;
  opendir my $dh, $CWD; # lexical opendir, so no closedir needed
  print "In: $CWD\n";

  while (my $entry = readdir $dh) {
    next if ($entry =~ /^\.+$/);
    # other exclusion tests    

    if (-d $entry) {
      walk_dir($entry);
    } elsif (-f $entry) {
      do_something($entry);
    }
  }

}