如果命令在变量中,则在子shell中执行管道命令不起作用

时间:2012-08-27 01:12:39

标签: bash pipe

以下代码可以正常使用

#!/bin/bash

while true; do
     echo $(pwd | awk '{print "dir: "$1}')
     sleep 1
done

但我需要将命令放在变量中,就像这样。

#!/bin/bash                                                                     

COMMAND=pwd | awk '{print "dir: "$1}'
while true; do
     echo $($COMMAND)
     sleep 1
done

第一个代码段执行正常,并打印类似dir: /present/directory的内容。第二个片段只打印空白行。为什么这样,我该如何解决?

我使用的命令没有意义。我只需要使用管道并选择这些命令来演示它。

2 个答案:

答案 0 :(得分:5)

如果您喜欢危险地生活,答案是eval

#!/bin/bash                                                                     

COMMAND="pwd | awk '{print \"dir: \"\$1}'"
while true
do
    echo $(eval $COMMAND)
    sleep 1
done

为什么危险?如果你接受来自天真(更不用说恶意)用户的命令字符串,那么很难控制eval发生的事情。还要注意我必须引用包含命令的字符串。

为什么问题中的第2版没有工作?

COMMAND=pwd | awk '{print "dir: "$1}'

这句话并没有按照你的想法做到,直言不讳。

它是一个管道,第一个'command'是字符串赋值COMMAND=pwd,被视为空命令的环境变量,它不向awk发送数据。完成后,$COMMAND中的值是一个空字符串(两者都是因为管道使用的子shell,因为COMMAND=pwd仅在执行(空)命令时应用了),所以循环回应执行空字符串的结果 - 这也是一个空字符串。因此空白行。


  

这对我有用,但我在AWK打印输出中添加标签时遇到问题,我认为这是因为转义。如果我有COMMAND="ls -l | awk '{print \$8 \$9}'",我如何在\t$8之间插入标签$9

哎哟,哎呀!

如果您在$8$9之间的来源中有一个标签,awk会将其视为通用空格,并将$8$9粘贴在一起在输出中。要在输出中获取标签,您需要包含标签的带引号的字符串,或者在两个字段之间包含\t的带引号的字符串,或者您将使用合适的printf()格式字符串。

所以,原则上,你可能想要:

COMMAND='ls -l | awk '\''{printf "%s\t%s\n", $8, $9}'\'

我已经切换到字符串周围的单引号,因为它们更容易理解。无论您使用单引号还是双引号,命令本身都包含两者,因此您必须担心如何进行转义。 '\''序列是将单引号嵌入单引号字符串的方式;第一个单引号结束当前单引号字符串;反斜杠单引号添加单引号,最后一个单引号重新启动单个字符串。最后的序列'\'也可以写成'\''',但最后两个引号是一个零长度的单引号字符串,所以我放弃它们。

这会产生带有标签的不错输出;您可以通过运行验证:

eval $COMMAND

echo $(eval $COMMAND)完全撤消所有细心的间距,将整个文件列表和时间展平为一行,每个'word'与下一个空格隔开。

您可以通过强制echo尊重间距(和换行符)来解决该问题:

echo "$(eval $COMMAND)"

但是这提出了“为什么不只使用eval $COMMAND?”的问题

(测试:bash 3.2.48 Mac OS X 10.7.4。)

答案 1 :(得分:0)

使用单引号使其中的所有内容都是文字的。这意味着$不会被评估。

然而,在第二个例子中,您通过说$($ COMMAND)强制重新评估。这导致'... $ 1'再次被处理,这次没有引号。那么 - 瞧!它有效!

您可以通过切换“和”,使用双引号(以及使用\“内部或单引号)来解决此问题。

希望这有帮助,

Ĵ