【问题标题】:Why is '$_' the same as $ARGV in a Perl one-liner?为什么 '$_' 与 Perl 单行中的 $ARGV 相同?
【发布时间】:2014-01-24 04:33:34
【问题描述】:

我在尝试在 Perl 单行中打印单引号时遇到了这个问题。我最终发现你必须用'\'' 来逃避它们。这是一些代码来说明我的问题。

让我们从打印一个文本文件开始。

perl -ne 'chomp; print "$_\n"' shortlist.txt

red
orange
yellow
green
blue

现在让我们为每一行打印文件名。

perl -ne 'chomp; print "$ARGV\n"' shortlist.txt

shortlist.txt
shortlist.txt
shortlist.txt
shortlist.txt
shortlist.txt

然后我们可以在每一行加上单引号。

perl -ne 'chomp; print "'$_'\n"' shortlist.txt

shortlist.txt
shortlist.txt
shortlist.txt
shortlist.txt
shortlist.txt

等等,没有用。让我们再试一次。

perl -ne 'chomp; print "'\''$_'\''\n"' shortlist.txt

'red'
'orange'
'yellow'
'green'
'blue'

所以我现在开始工作了。但我仍然对为什么 '$_' 评估为程序名称感到困惑。也许这很容易,但有人可以解释或链接到一些文档吗?

编辑:我在 Red Hat 5 上运行 Perl 5.8.8

【问题讨论】:

  • 在您的第三个示例中,$_ 在引号之外,这意味着在形成字符串并传递给perl -ne之前对其进行评估
  • 这是一个shell特性,与Perl无关。尝试echo foo; echo $_(在我的bash 或sh 上重复foo 两次:$_ 是最后一个参数)。所以重要的问题是:您使用的是什么外壳?

标签: perl bash sh


【解决方案1】:

在您的 shell 中,'chomp; print "'$_'\n"' 会生成一个字符串,该字符串是

  1. chomp; print "(单引号内的第一个序列),
  2. 其变量$_的值,以及
  3. \n"(单引号内的第二个序列)。

bash 中,$_ “...展开到前一个命令的最后一个参数,展开后......”。由于这恰好是shortlist.txt,因此将以下内容传递给perl

chomp; print "shortlist.txt\n"

例如,

$ echo foo
foo

$ echo 'chomp; print "'$_'\n"'
chomp; print "foo\n"

请注意,不应使用上述机制将值传递给 Perl 单行器。您不应该从 shell 生成 Perl 代码。请参阅How can I process options using Perl in -n or -p mode? 了解如何为单行提供参数。

【讨论】:

    【解决方案2】:

    您在单行中使用单引号来保护您的 Perl 代码不被 shell 评估。在这个命令中:

    perl -ne 'chomp; print "'$_'\n"' shortlist.txt
    

    您关闭了$_ 之前的单引号,因此shell 将$_ 扩展为the last argument to the previous command。在您的情况下,这恰好是您的输入文件的名称,但如果您先运行不同的命令,输出会有所不同:

    $ echo foo
    $ perl -ne 'chomp; print "'$_'\n"' shortlist.txt
    foo
    foo
    foo
    foo
    foo
    

    【讨论】:

      【解决方案3】:

      出于这个原因,我尽量避免在一个衬里中使用引号。我尽可能使用广义引用:

      % perl -ne 'chomp; print qq($_\n)'
      

      虽然我可以通过-l 开关免费获得换行符来避免这种情况:

      % perl -nle 'chomp; print $_'
      

      如果我不理解单行,我使用-MO=Deparse 来看看 Perl 认为它是什么。前两个是您所期望的:

      % perl -MO=Deparse -ne 'chomp; print "$_\n"' shortlist.txt
      LINE: while (defined($_ = <ARGV>)) {
          chomp $_;
          print "$_\n";
      }
      -e syntax OK
      
      % perl -MO=Deparse -ne 'chomp; print "$ARGV\n"' shortlist.txt
      LINE: while (defined($_ = <ARGV>)) {
          chomp $_;
          print "$ARGV\n";
      }
      -e syntax OK
      

      您在看到问题的地方看到了一些有趣的东西。该变量在perl 看到它之前就消失了,并且在它的位置有一个常量字符串:

      % perl -MO=Deparse -ne 'chomp; print "'$_'\n"' shortlist.txt
      
      LINE: while (defined($_ = <ARGV>)) {
          chomp $_;
          print "shortlist.txt\n";
      }
      -e syntax OK
      

      您的修复也很奇怪,因为 Deparse 将变量名放在大括号中,以将其与旧的包说明符 ' 分开:

      % perl -MO=Deparse -ne 'chomp; print "'\''$_'\''\n"' shortlist.txt
      LINE: while (defined($_ = <ARGV>)) {
          chomp $_;
          print "'${_}'\n";
      }
      -e syntax OK
      

      【讨论】:

      • Nit: perl -nle 'chomp; print $_' 在使用$_ 时相当不一致,而-nl 暗示chomp,所以perl -nle 'chomp; print $_' 变成perl -nle 'chomp; print' 然后perl -nle 'print',然后perl -ple 1 ,然后perl -ple1。但是为什么-lperl -pe1cat。我不知道您为什么建议其中一种简化而不是其他简化。 (我完全不知道您为什么建议进行任何简化。)
      • 尽管实际代码很傻,但它是 OP 使用的。我使用原始代码展示了一些东西,以便对 OP 正在做的特定事情进行最少的更改。也许他简化了他的剧本以完成一项更大的任务。 耸耸肩
      • 您在perl -MO=Deparse 'chomp; print "'$_'\n"' shortlist.txt 线上缺少-ne,是的,这段代码有点傻,但它只是为了说明问题而对更大任务的简化。谢谢!
      • Re “我使用原始代码展示了一些东西,以便对 OP 正在做的特定事情进行最少的更改。”你在说什么?唯一需要的更改是在输出中添加引号,而 -l 没有这样做,因此这绝对不是实现所需目标的最小方法。
      • @ikegami - Matt 很高兴并且发现它很有用,所以也许你可以从我遇到的任何个人问题中继续前进,不要再反对所有证明它没有用的证据。
      猜你喜欢
      • 2022-07-14
      • 2012-01-31
      • 2018-04-15
      • 2015-10-24
      • 2021-03-25
      • 2013-05-17
      • 2015-09-14
      • 1970-01-01
      • 2023-04-08
      相关资源
      最近更新 更多