在嵌套的单引号,反引号和引用中转义美元符号

时间:2018-03-30 11:34:23

标签: bash perl dpkg perl5

我想使用Perl在文件的每一行上运行shell命令。该文件包含包名称,例如firefox

这是对单个包名称的调用;它有效:

dpkg-query -W -f='${binary:Package}_${Version}' firefox

如果我从单行"文件"像这样它不再起作用了:

echo firefox | perl -ne \
  'print `dpkg-query -W -f="${binary:Package}_${Version}" $_` . "\n"'

输出为_\n。那么在这些嵌套引号中逃避这些美元的正确方法是什么,以便dpkg-query接收不带shell或bash的逐字字符串${binary:Package}_${Version}将其解释为"他们的"变量?我尝试了各种排列但到目前为止无济于事。输出的格式应为firefox_59.0.2+build1-0ubuntu0.16.04.1\n

2 个答案:

答案 0 :(得分:6)

shell命令中的Perl脚本中的shell命令中有一个格式字符串。这种嵌套水平使逃避变得非常困难,因此最好的解决方案是摆脱不必要的水平。

这里,最简单的方法是摆脱内部shell命令。您不需要通过shell运行dpkg命令,并且可以直接执行它。这将避免在Perl中处理任何类型的shell元字符。例如:

perl -ne 'chomp; system q(dpkg-query), q(-W), q(-f=${binary:Package}_${Version}), $_; print qq(\n)'

这将直接将命令输出打印到STDOUT,而不首先捕获它。与使用反引号一样,这不会进行任何错误处理。要获得真正的退出代码,您需要$? >> 8。因此,通过适当的错误处理,命令将如下所示:

perl -ne '
  chomp;
  @command = (q(dpkg-query), q(-W), q(-f=${binary:Package}_${Version}), $_); 
  system(@command) == 0 or die sprintf qq(Command [%s] exited with status %d\n), qq(@command), $? >> 8;
  print qq(\n)'

答案 1 :(得分:1)

有两个主要问题:

  1. 您需要以某种方式将'放入Perl代码中的字符串中,该代码已针对shell引用'
  2. 您需要在反引号中转义$,因为它们对Perl具有特殊含义。
  3. 让我们先从第二个问题开始。

    dpkg-query -W -f='${binary:Package}_${Version}' firefox
    

    是正常的命令行。把它包裹在反引号中给了我们

    print `dpkg-query -W -f='\${binary:Package}_\${Version}' $_` . "\n"
    

    在Perl。

    现在我们要在命令行上将该代码传递给perl,这需要为shell引用所有这些:

    perl -ne 'print `dpkg-query -W -f='\''\${binary:Package}_\${Version}'\'' $_` . "\n"'
    

    如果这看起来令人困惑,原始字符串中的每个'都会在引用版本中变为'\''。这是因为第一个'结束了我们所在的单引号字符串,然后\'指定了单个转义引号,然后'再次启动引用部分。 (如果你仔细观察,你可以在SO的语法高亮中看到它。)

    我们可以应用另一种简化方式。 $_中的传入行仍然包含一个我们不需要的尾随换行符(或者想要:如果$_之后有任何内容,它会破坏命令)。我们可以使用-l选项将其删除,这也会使print默认输出\n

    echo firefox | perl -lne \
      'print `dpkg-query -W -f='\''\${binary:Package}_\${Version}'\'' $_`'