2

以下代码工作正常

#!/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. 第二个片段只打印空行。为什么会这样,我该如何解决?

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

4

2 回答 2

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=pwd被视为空命令的环境变量,它不向awk. 完成后,in 的值$COMMAND是一个空字符串(既是因为与管道一起使用的子shell,也是因为COMMAND=pwd仅在执行(空)命令时应用),所以循环回显了执行空字符串的结果——这也是一个空字符串。因此,空白行。


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

哎哟,哎呀!

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

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

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

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

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

eval $COMMAND

完全取消了所有仔细的echo $(eval $COMMAND)间距,将整个文件和时间列表展平为一行,每个“单词”与下一个“单词”用空格分隔。

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

echo "$(eval $COMMAND)"

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

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

于 2012-08-27T01:16:01.210 回答
0

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

但是,在第二个示例中,您通过说 $($COMMAND) 来强制重新评估。这会导致再次处理 '...$1',这次没有引号。那么——瞧!有用!

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

希望这可以帮助,

Ĵ

于 2012-08-27T01:17:54.743 回答