【问题标题】:How to read directories and sub-directories without knowing the directory name in perl?如何在不知道 perl 中的目录名称的情况下读取目录和子目录?
【发布时间】:2013-12-31 07:59:22
【问题描述】:

您好,我想在不知道目录名称的情况下读取目录和子目录。当前目录是“D:/Temp”。 'Temp' 具有像 'A1'、'A2' 这样的子目录。同样,“A1”具有像“B1”、“B2”这样的子目录。同样,“B1”具有像“C1”、“C2”这样的子目录。 Perl 脚本不知道这些目录。因此,它必须首先找到目录,然后在目录“C1”中一次读取一个文件,一旦在“C1”中读取所有文件,它应该更改为“C2”目录。我在这里尝试了以下代码,我不想读取数组(@files)中的所有文件,但一次需要一个文件。数组中的 @dir 元素应为 fallows。

$dir[0] = "D:/Temp/A1/B1/C1"
$dir[1] = "D:/Temp/A1/B1/C2"
$dir[2] = "D:/Temp/A1/B2/C1"

下面是我试过的代码。

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

    my $dir = "D:/Temp";
    my @dir = File::Find::Rule->directory->in($dir);
    print Dumper (\@dir);
    my $readDir = $dir[3];
    opendir ( DIR, $readDir ) || die "Error in opening dir $readDir\n";
    my @files = grep { !/^\.\.?$/ } readdir DIR;
    print STDERR "files: @files \n\n";

    for my $fil (@files) {
        open (F, "<$fil");
        read (F, my $data);
        close (F);
        print "$data";
    }

【问题讨论】:

  • 是否有“D:/Temp/A1/B2/C2”?
  • 考虑在您的问题中添加 recursion、filesystems 和 windows 标签。

标签: perl


【解决方案1】:
use File::Find;

use strict;
use warnings;

my @dirs;
my %has_children;

find(sub {
    if (-d) {
        push @dirs, $File::Find::name;
        $has_children{$File::Find::dir} = 1;
    }
}, 'D:/Temp');

my @ends = grep {! $has_children{$_}} @dirs;

print "$_\n" for (@ends);

【讨论】:

  • 但是元素的索引并不像我期望的那样。对我来说,第一个元素应该是 $dir[0] = "D:/Temp/A1/B1/C1" 但根据您的代码,我得到 $dir[0] = "D:/Temp"。
  • @drp - 所以,您只想要那些本身没有子文件夹的文件夹的完整路径?
  • 如果你不想要有子目录的目录,那么你可以过滤掉它们。已编辑帖子以考虑到这一点。
【解决方案2】:

您的目标:找到那些本身没有子目录的目录的绝对路径。

我将把这些感兴趣的目录称为终端目录。这是一个功能的原型,我相信它可以提供您正在寻找的便利。该函数将其结果作为列表返回。

my @list = find_terminal_directories($full_or_partial_path);

这是find_terminal_directories() 的一个实现。请注意,此实现不需要使用任何全局变量。另请注意使用称为递归的私有帮助函数。

在我的 Windows 7 系统上,对于输入目录 C:/Perl/lib/Test,我得到了输出:

== List of Terminal Folders ==
c:/Perl/lib/Test/Builder/IO
c:/Perl/lib/Test/Builder/Tester
c:/Perl/lib/Test/Perl/Critic

== List of Files in each Terminal Folder: ==
c:/Perl/lib/Test/Builder/IO/Scalar.pm
c:/Perl/lib/Test/Builder/Tester/Color.pm
c:/Perl/lib/Test/Perl/Critic/Policy.pm

实施

#!/usr/bin/env perl

use strict;
use warnings;
use Cwd qw(abs_path getcwd);

my @dir_list = find_terminal_directories("C:/Perl/lib/Test");
print "== List of Terminal Directories ==\n";
print join("\n", @dir_list), "\n";

print "\n== List of Files in each Terminal Directory: ==\n";
for my $dir (@dir_list) {
    for my $file (<"$dir/*">) {
        print "$file\n";
        open my $fh, '<', $file or die $!;
        my $data = <$fh>;  # slurp entire file contents into $data
        close $fh;

        # Now, do something with $data !
    }
}

sub find_terminal_directories {
    my $rootdir = shift;

    my @wanted;
    my $cwd = getcwd();
    chdir $rootdir;
    find_terminal_directories_helper(".", \@wanted);
    chdir $cwd;
    return @wanted;
}

sub find_terminal_directories_helper {
    my ($dir, $wanted) = @_;
    return if ! -d $dir;
    opendir(my $dh, $dir) or die "open directory error!";
    my $count = 0;
    foreach my $child (readdir($dh)) {
        my $abs_child = abs_path($child);
        next if (! -d $child || $child eq "." || $child eq "..");
        ++$count;
        chdir $child;
        find_terminal_directories_helper($abs_child, $wanted);  # recursion!
        chdir "..";
    }
    push @$wanted, abs_path($dir) if ! $count;  # no sub-directories found!
}

【讨论】:

    【解决方案3】:

    也许以下内容会有所帮助:

    use strict;
    use warnings;
    use File::Find::Rule;
    
    my $dir = "D:/Temp";
    local $/;
    
    my @dirs =
      sort File::Find::Rule->exec( sub { File::Find::Rule->directory->in($_) == 1 }
      )->directory->in($dir);
    
    for my $dir (@dirs) {
        for my $file (<"$dir/*">) {
            open my $fh, '<', $file or die $!;
            my $data = <$fh>;
            close $fh;
            print $data;
        }
    }
    
    • local $/; 让我们slurp 将文件的内容放入一个变量中。如果您只想阅读第一行,请将其删除。
    • exec() 中的sub 仅用于传递不包含目录的目录
    • sort 用于按照您想要的顺序排列这些目录
    • 一个文件glob&lt;"$dir/*"&gt;用于获取每个目录下的文件

    编辑:已修改代码以仅查找“终端目录”。感谢 DavidRR 对此规范的说明。

    【讨论】:

    • OP 澄清说,他想处理 only 我称之为 terminal 目录 的那些目录......即那些本身不包含目录的目录。 (请参阅my answer。)File::Find::Rule 非常强大...您可以“轻松”修改您的答案以添加额外的过滤以仅保留终端目录吗?
    • @DavidRR - 我错过了这个规范,感谢您的澄清。已相应修改脚本。
    • 太棒了!这是File::Find::Rule 的 perldoc。
    • @DavidRR - 你的回答对我帮助很大。现在我还有 1 个问题。使用您的代码,我将获得终端目录,其中有许多二进制文件。我应该一次读取一个文件,直到目录结束,然后它应该翻转到下一个目录。有时,新文件会不断添加到当前的工作目录中。我使用数组将文件复制到其中,但是当新文件添加到此目录时,数组中将不可用。请帮助如何做到这一点。文件命名约定是“second-microsec.extension”
    • drp - 我想你是想引起 Kenosis 的注意。他非常简洁的回答当然值得你考虑。请注意,$file 包含每个终端文件夹中每个文件的完整路径。 (我也更新了我的答案来证明这一点。)我希望这就是你要找的。​​span>
    【解决方案4】:

    我会使用File::Find

    示例脚本:

    #!/usr/bin/perl
    
    use strict; 
    use warnings;
    
    use File::Find;
    
    my $dir = "/home/chris";
    
    find(\&wanted, $dir);
    
    sub wanted {
        print "dir: $File::Find::dir\n";
        print "file in dir: $_\n";
        print "complete path to file: $File::Find::name\n";
    }
    

    输出:

    $ test.pl
    dir: /home/chris/test_dir
    file in dir: test_dir2
    complete path to file: /home/chris/test_dir/test_dir2
    dir: /home/chris/test_dir/test_dir2
    file in dir: foo.txt
    complete path to file: /home/chris/test_dir/test_dir2/foo.txt
    ...
    

    【讨论】:

    【解决方案5】:

    使用反引号,将子目录和文件写入一个名为 filelist 的文件:

    `ls -R $dir > filelist`
    

    【讨论】:

    • OP 如何在 Windows 环境中执行此操作?
    猜你喜欢
    • 2014-05-08
    • 2022-01-13
    • 1970-01-01
    • 2019-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-18
    • 1970-01-01
    相关资源
    最近更新 更多