【问题标题】:How can I implement Unix grep in Perl?如何在 Perl 中实现 Unix grep?
【发布时间】:2010-10-03 02:51:58
【问题描述】:

如何在 Perl 中实现 Unix 的grep?我尝试使用 Perl 的内置 grep。这是不起作用的代码:

$pattern = @ARGV[0];
$file= @ARGV[1];

open($fp,$file);

@arr = <$fp>;

@lines = grep $pattern, @arr;

close($fp);
print @lines;

顺便说一句,我只尝试基本的grep 功能而不是全功能,其次我不想自己进行字符串解析。我想使用内置的grep 或 Perl 的一些功能。

提前致谢:)

【问题讨论】:

    标签: regex perl grep


    【解决方案1】:

    由于您已经接受了一个答案,我正在写这个答案以供将来寻找类似问题但不完全是您的问题的读者参考:

    正如人们已经回答的那样,用perl模拟grep的方法是使用在线方法。 对于将 perl 用作“更好”的 grep(以及查找和剪切...),我推荐这本书 minimal perl,你很幸运,因为 'perl as a "better" grep' 的章节是示例章节之一。

    这里有更多受本书启发的示例:

    perl -wnle '/foo/ and print' null.txt  # normal grep
    perl -wnle '/foo/ and print "$ARGV: $_"' null.txt # grep -H
    perl -wnle '/foo/ and print $ARGV and close ARGV' null_1.txt null_2.txt # grep -l
    

    在最后一个示例中,ARGV 是当前文件句柄,与 -l 一样,您有兴趣查找匹配的文件,您可以打印文件名并在文件中的第一个匹配项之后查找下一个文件。

    您也可以按段落而不是按行搜索:

    $ perl -00 -wnl -e '/\bBRIBE\b/i and print;' SenQ.testimony
    I knew I'd be in trouble if
    I ACCEPTED THE BRIBE!
    So I did not.
    
    My minimum bribe is $100k, and she only offered me $50k,
    so to preserve my pricing power, I refused it.
    

    或者只找到第一个匹配项:

    $ perl -00 -wnl -e '/\bBRIBE\b/i and close ARGV;' SenQ.testimony
    I knew I would be in trouble if
    I ACCEPTED THE BRIBE!
    So I did not.
    

    最后,如果您询问 grep 和 perl,我想我应该提到 ACK。它在 perl 中实现了 grep 功能并对其进行了扩展。这是一个很棒的工具,而且您还可以将它作为 CPAN 包使用。我一直用作命令行,我不知道你是否可以直接从你的 perl 程序中访问它的方法,但这会非常好。

    【讨论】:

      【解决方案2】:

      在 Perl 中,我们使用 @ 来引用整个数组。但是要引用单个元素,它们是标量,我们使用$

      因此,您需要在这些行中使用 $ 而不是 @

      $pattern = @ARGV[0];
      $file= @ARGV[1];
      

      还有

      这个

      @lines = grep $pattern, @arr;
      

      应该是

      @lines = grep /$pattern/, @arr;
      

      Perl 中的grep 具有以下一般语法:

      grep EXPR,LIST
      

      它为LIST 的每个元素计算EXPR,并返回由表达式计算为真的元素组成的列表值。

      在您的情况下,EXPR 正在数组@arr 中搜索模式$pattern。要进行搜索,您需要使用不带//PATTERN/,字符串$pattern 将被评估为真或假。

      【讨论】:

      • @coddadict,您在修复代码中的错误方面做得很好,但是这个问题和用户使用的方法将其转换为准“xy 问题”,所以这可能是灌输的情况之一不是题外话。我会建议不要同时读取整个文件(他想要模拟面向行的 grep),我会建议他使用 grep {} 而不是 grep() 只是为了养成一个好习惯,而这三个论据公开。甚至进一步向他展示在线方法(或与 while 逐行)将是一个加分。
      【解决方案3】:

      当然,codaddict 的回答是对的,但我想补充几点:

      您应该始终以这两行开始您的脚本:

      use strict;
      use warnings;
      

      使用三个参数打开并测试错误:

      open my $fh, '<', $file or die "unable to open '$file' for reading : $!";
      

      由于use strict,您必须声明所有变量。所以你的脚本会是这样的:

      #!/usr/bin/perl
      
      use strict;
      use warnings;
      
      my $pattern = $ARGV[0];
      my $file = $ARGV[1];
      
      open $fh, '<', $file or die "unable to open file '$file' for reading : $!";
      my @arr = <$fh>;
      close $fh;  # close as soon as possible
      
      my @lines = grep /$pattern/, @arr;
      
      print @lines;
      

      如果你的文件很大,你可以避免完全在内存中读取它:

      #!/usr/bin/perl
      use strict;
      use warnings;
      
      my $pattern = qr/$ARGV[0]/;
      my $file= $ARGV[1];
      print "pattern=$pattern\n";
      
      my @lines;
      open my $fh, '<', $file or die "unable to open file '$file' for reading : $!";
      while(my $line=<$fh>) {
          push @lines, $line if ($line =~ $pattern);
      }
      close($fh);
      print @lines;
      

      【讨论】:

      • 您可以简单地将 while 循环中的 'push @lines, $line' 替换为 'print $line' 并避免使用任何数组。如果你的文件是“大”的,那么某人或某事有一天会不可避免地触发一个返回几乎所有文件行的 grep。 (同样大:-)
      【解决方案4】:

      您可以直接在命令行上近似原始版本的grep-e 选项允许您在命令行上定义 Perl 脚本。 -n 选项大致如下包装您的脚本:while (&lt;&gt;){ SCRIPT }

      perl -ne 'print if /PATTERN/' FILE1 FILE2 ...
      

      grep 的近似值稍微好一点,会在每个打印匹配项前面添加文件名前缀。请注意,与上面的示例一样,此示例不会遇到打开任何文件的麻烦。相反,我们使用 Perl 的 &lt;&gt; 结构来遍历所有文件,$ARGV 变量提供当前文件名。

      use strict;
      use warnings;
      
      my $pattern = shift;
      
      while (my $line = <>){
          print $ARGV, ':', $line if $line =~ $pattern;
      }
      

      【讨论】:

      • 特殊变量$. 包含当前行号。因此,如果您也想打印它,您可以使用perl -ne 'print "$ARGV,$.:$_" if /PATTERN/' file1 file2
      【解决方案5】:

      基本的“grep”功能已经实现。 (=~)

      $string =~ /pattern/;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-07-03
        • 1970-01-01
        • 2016-12-05
        • 1970-01-01
        • 1970-01-01
        • 2012-09-02
        • 1970-01-01
        • 2016-09-21
        相关资源
        最近更新 更多