我想使用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
。
答案 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)
有两个主要问题:
'
放入Perl代码中的字符串中,该代码已针对shell引用'
。$
,因为它们对Perl具有特殊含义。让我们先从第二个问题开始。
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}'\'' $_`'