【问题标题】:Opening a directory and searching for a pattern in files打开目录并在文件中搜索模式
【发布时间】:2019-11-20 07:13:13
【问题描述】:

我的 Perl 脚本应该打开目录中的所有文件并在其中搜索模式并打印包含该模式的整行。 下面是代码。代码无法打开文件。

  my $dir = 'dir1/dir2';
    opendir (DIR, $dir) or die $!;
    my @dir = readdir DIR;
    foreach my $item (@dir) {
        open(FILE, "<", "file.txt")
        or die "Can't open < file.txt: $!";
        while($line= <FILE>) {
        print "$line" if $line=~ /pattern/;
    }
    close FILE;
    }
    closedir DIR;

请提出一些使它起作用的方法。

【问题讨论】:

  • readdir 只返回名称,而不是路径。因此,当您尝试打开file.txt 时,没有这样的事情——您需要说"$dir/file.txt"。另一种方法是将其添加到整个列表中,因此 my @dir = map { "$dir/$_" } readdir DIR; 和现在 @dir 中的文件具有正确的路径
  • 嗯,如果按照您的逻辑,您尝试读取每个目录中的 file.txt - 但不是目录中的每个文件(-ies)。您的描述与您的代码逻辑不匹配。
  • 我不确定 perl 脚本是否是解决您的任务的强制性要求。否则,您可以使用 find 和 grep 来获得相同的结果 find /some/dir -exec grep [pattern] {} \; -ls。此命令将搜索目录和子目录中的每个文件。
  • @PolarBear 也可以这样做,但是没有必要 find 因为 grep 可以指定要搜索的内容,这里很简单,grep [options] PATTERN dir1/dir2/* 其中 options 会决定如何呈现调查结果
  • 请注意:并非所有版本的 grep 都支持在子目录中进行递归搜索 - find/grep 工作了很长时间并且从未失败过。

标签: file perl


【解决方案1】:

readdir 只返回名称 (file.txt),而不是路径 (不是 dir1/dir2/path.txt)。

所以当程序试图打开$item 时,结果证明没有这样的事情——需要打开"$dir/$item"。或者,将其添加到文件列表中的每个条目,然后使用正确的路径处理

use warnings;
use strict;

my $dir = 'dir1/dir2';

opendir (my $dh, $dir) or die $!;
my @entries = map { "$dir/$_" } readdir $dh;
closedir $dh;

foreach my $item (@entries) {
    next if not -f $item;
    open(my $fh, "<", $item) or die "Can't open < $item: $!";

    while(my $line = <$fh>) {
        print $line if $line =~ /pattern/;
    }
    close $fh;
}

请注意,我会跳过非常规文件的条目(not -f,请参阅filetest operators),并使用lexical filehandes instead of typeglobsmy $fh 而不是FILE)。

获取条目列表的另一个选项是glob(参见File::Glob),它会返回路径:

my @entries = glob $dir;  # dir1/dir2/file.txt  (etc)

但是,如果您确实要提前获取整个文件列表,而不是一次读取和处理一个项目,那么您不妨立即根据需要对其进行过滤,所以

my @files = grep { -f } glob $dir;

您现在只有常规文件。

当然也可以在上面的程序中过滤整个列表。可以在map 本身中做到这一点

opendir (my $dh, $dir) or die $!;
my @files = map { -f "$dir/$_" ? "$dir/$_" : () } readdir $dh;
closedir $dh;

这里map 也可以通过一个小技巧作为过滤器:当条目不是普通文件时返回的空列表() 在返回的列表中被展平,因此实际上消失了,所以这相当于在这种情况下不返回任何东西。或者,像往常一样将grep 链接到map

my @files = grep { -f } map { "$dir/$_" } readdir $dh;

这确实(又一次)通过了列表,但这是优化的,几乎没有问题(除非我们有 huge 文件列表——这本身就会造成问题——并且是经常这样做)。


我还想提一下,也有很好的模块。

Path::Tiny 的示例

use Path::Tiny;

my $it = path($dir)->iterator;

while (my $entry = $it->()) { 
    # it omits . and ..
}

这是一个“惰性”迭代器。有关此模块提供的许多其他内容,请参阅文档等。


而不是那个file.txt

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-25
    • 1970-01-01
    • 2017-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-17
    相关资源
    最近更新 更多