【问题标题】:What should every Perl hacker know about perl -ne?每个 Perl 黑客都应该了解 perl -ne 什么?
【发布时间】:2014-02-22 00:31:57
【问题描述】:

多年来,我一直在使用带有-ne 选项的 Perl 命令行,主要用于以 sed 无法处理的方式处理文本文件。示例:

cat in.txt | perl -ne "s/abc/def/; s/fgh/hij/; print;" > out.txt

我不知道我是从哪里学来的,今天才看到perlrun,发现还有其他形式(例如perl -pe)。

关于perl -ne,我还应该知道什么?

【问题讨论】:

  • perl -pe 适合您的示例代码。使用-pe 并删除print 语句
  • 好吧,这一切都在 perlrun 中。每个 Perl 黑客都应该阅读文档。 :)
  • 也可以跳过“cat in.txt |”部分,只需将 in.txt 放在命令行末尾,因为“wokka”操作符将选取在 cmd 行末尾命名的文件, stdin.
  • Perl 黑客(以及非 Perl 黑客)应该了解 UUOC (sial.org/howto/shell/useless-cat) ...
  • 我应该说这是一个非常简单的例子,我通常不会只关注这样的东西,一个 perl -ne 更有可能出现在一堆各种流水线命令的中间类型。不过感谢您的 cmets。

标签: perl command-line


【解决方案1】:

perl -ne 'CODE'相当于程序

while (<>) {
    CODE
}

perl -ane 'CODE'perl -F/PATTERN/ -ane 也是值得了解的好习惯。它们相当于

while (<>) {
    @F = split /\s+/, $_;
    CODE
}

while (<>) {
    @F = split /PATTERN/, $_;
    CODE
}

示例:高级 grep

perl -ne 'print if/REGEX1/&&!/REGEX2/&&(/REGEX3/||/REGEX4/&&!/REGEX5/)' input

perl -F/,/ -ane 'print if $F[2]==4&&$F[3]ge"2009-07-01"&&$F[3]lt"2009-08-01"' file.csv


使用不匹配大括号的一个特别聪明的例子是here

【讨论】:

  • 出于好奇,你知道是什么让他们选择@F吗?
  • 我猜“F”代表字段。我认为这也可能是一个 awk 约定。
  • perldoc perlvar 说“数组@F 包含每一行的字段”
【解决方案2】:

关于perl -neperl -pe 脚本有一件重要的事情需要了解:它们隐式使用&lt;&gt;

“为什么这很重要?”你可能会问。

神奇的&lt;&gt; 运算符使用2 arg 形式的open。如果您还记得,2 arg open 在一个参数中包含带有文件名的模式规范。对open FILE, $foo 的旧式调用容易受到文件模式的操纵。在这种情况下,一个特别有趣的模式是|——您打开一个指向您执行的进程的管道的句柄。

您可能会想“大不了!”,但确实如此。

  • 想象一个由 root 执行的 cron 作业,以清理某个目录中的日志文件。
  • 脚本以script * 调用。
  • 想象一下该目录中有一个名为 |rm -rf / 的文件。

会发生什么?

  1. shell 扩展了*,我们得到script file_1 file_2 '|rm -rf /' file_4
  2. 脚本处理file_1file_2
  3. 接下来,它打开一个rm -rf / 的 STDIN 句柄。
  4. 大量磁盘活动随之而来。
  5. file_4 已不存在,因此无法打开。

当然,可能性是无穷无尽的。

您可以阅读more discussion of this issue at Perlmonks

故事的寓意:小心&lt;&gt; 运算符

FWIW,我刚刚确认这仍然是 perl 5.10.0 的问题。

【讨论】:

  • 这是一些严肃的事情。有没有人说过如果使用 3 参数 open 和显式“读取”模式来实现 wokka 操作符会有什么缺点?
  • 好的,我阅读了 perlmonks 线程。少数人坚持认为精神错乱是一个特征。 “您希望这只是打开要读取的文件列表,但它做的更多,我们不在乎您希望获得安全、正交和简单的东西”。需要有某种“理智的钻石”编译指示/模块。我赞成这个答案,高于我自己的答案,并希望它最终能排在榜首。
  • @Roboprog 有一个 CPAN 模块:ARVG::readonly.
【解决方案3】:

您可以指定多个 -e 子句。有时我的命令行会随着我优化搜索/提取/处理操作而开始增长。如果你输入错误,你会得到一个“行号”,告诉你哪个 -e 有错误。

当然,有些人可能会争辩说,如果您有超过一两个 -e 子句,也许您应该将它的任何内容放入脚本中,但有些东西真的只是扔掉了,所以何必费心呢。

perl -n -e 'if (/good/)' -e '{ system "echo $_ >> good.txt"; }' \
-e 'elsif (/bad/)' -e '{ system "echo $_ >> bad.txt"; }' \
-e 'else' -e '{ system "echo $_ >> ugly.txt"; }' in.txt another.txt etc.txt

大概你会做一些比 grep / egrep 更简单的事情 :-)

【讨论】:

    【解决方案4】:

    -i 选项可让您进行内联更改:

     perl -i -pe 's/abc/def/; s/fgh/hij/' file.txt
    

    或保存备份:

     perl -i.bak -pe 's/abc/def/; s/fgh/hij/' file.txt
    

    【讨论】:

      【解决方案5】:

      我喜欢将perl -n 视为选择输入的特定位,将perl -p 视为map 用于输入的所有行。

      正如您所观察到的,-p-n 的效果是可能的,我们可以反过来模拟:

      $ echo -e "1\n2\n3" | perl -pe '$_="" 如果 $_ % 2 == 0'
      1
      3

      使用next 跳过行似乎更自然,但-p 将代码包装在

      LINE:
      while (<>) {
          ...     # your program goes here
      } continue {
          print or die "-p destination: $!\n";
      }
      

      按照设计,next 运行 continue 块:

      如果有continue BLOCK,它总是在条件即将被再次评估之前执行。因此它可以用来增加循环变量,即使循环已经通过next 语句继续。

      -l 开关有两个方便的效果:

      1. 使用-n-p,自动chomp 每个输入记录。
      2. 设置$\,这样每个print 都会隐式添加一个终止符。

      例如,要获取/etc/services 中提到的前 10 个 UDP 端口,您可能会

      perl -ane 'print $F[1] if $F[1] =~ /udp/' /etc/services |头

      但是哎呀:

      7/udp9/udp11/udp13/udp17/udp19/udp37/udp39/udp42/ud...

      更好:

      $ perl -lane 'print $F[1] if $F[1] =~ /udp/' /etc/services |头
      7/udp
      9/udp
      11/udp
      13/udp
      17/udp
      19/udp
      37/udp
      39/udp
      42/udp
      53/udp

      记住-n-p也可以在shebang行中,所以要将上面的oneliner保存为脚本:

      #! /usr/bin/perl -lan
      
      BEGIN {
        @ARGV = ("/etc/services") unless @ARGV;
        open STDOUT, "|-", "head" or die "$0: head failed";
      }
      
      print $F[1] if $F[1] =~ /udp/
      

      【讨论】:

        【解决方案6】:

        我最喜欢的 Perl one liner 参考资料(以及该短语在 Google 上的热门)涵盖 perl -ne:http://novosial.org/perl/one-liner/

        【讨论】:

          【解决方案7】:

          我经常使用sedawk 但我真的很喜欢perl 匹配模式杀手功能:

          $ cat my-input.txt
          git 111 HERE 2222 voila 333
          any 444 HERE none start 555 HERE 6
          svn 777 aaaa 8888 nothing
          two 222 HERE 9999 HERE 0000
          
          $ perl -nle 'print $a if (($a)=/HERE ([0-9]+)/)' my-input.txt
          2222
          6
          9999
          

          【讨论】:

            猜你喜欢
            • 2011-02-17
            • 1970-01-01
            • 2010-11-19
            • 1970-01-01
            • 1970-01-01
            • 2015-12-16
            • 2011-08-21
            • 2015-01-15
            相关资源
            最近更新 更多