我正在尝试在bash脚本中捕获远程执行命令的输出,以便该脚本可以对结果进行某种处理,并且对结果感到困惑:
$ COMMAND="ssh localhost \"echo 'hello'\""
$ OUTPUT=$($COMMAND)
bash: echo 'hello': command not found
使用反引号会产生稍微不同的结果:
$ `ssh localhost "echo 'hello'"`
-bash: hello: command not found
但是命令运行时没有任何包装:
$ ssh localhost "echo 'hello'"
hello
我怀疑我误解了有关如何执行这些命令的一些基本知识。我在这里做错了什么吗?还是有更好的方法?
答案 0 :(得分:2)
执行以下操作时不会评估行号("..."
)
$($COMMAND)
远程Bash看到的是两个参数:"echo
和'hello'"
,而不是您期望的echo 'hello'
。
在您的情况下,我建议使用数组:
COMMAND=( ssh localhost "echo 'hello'" )
OUTPUT=$( "${COMMAND[@]}" )
(...)
中的单词是数组的元素。在这种情况下,echo 'hello'
被视为单个元素:在赋值期间立即评估引号。然后,当您执行"${COMMAND[@]}"
时,每个元素都将作为参数传递而没有改变。
还有其他几种方法可以解决此问题,例如使用eval
以便对引号进行评估,但是以我的经验,使用数组是构建和执行命令的最安全,最简单的方法。使用数组可以很容易地防止shell注入攻击,防止不必要的扩展,防止在有空格的情况下破坏单词。唯一的缺点是数组并非在每个shell中都可用。
原因是扩展名(即$COMMAND
)不评估引号。
这个简单的示例说明了这一点,在该示例中,我们每行打印一个参数:
$ COMMAND="ssh localhost \"echo 'hello'\""
$ for x in $COMMAND; do echo "$x"; done
ssh
localhost
"echo
'hello'"
通过使用数组代替:
$ COMMAND=( ssh localhost "echo 'hello'" )
$ for x in "${COMMAND[@]}"; do echo "$x"; done
ssh
localhost
echo 'hello'
在数组中,echo 'hello'
在一行上(即它是单个参数),而用一个简单的字符串代替了"echo
和'hello'"
。