首页 > 解决方案 > 捕获远程 ssh 命令失败的输出

问题描述

我正在尝试在 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

我怀疑我误解了关于如何执行这些命令的一些非常基本的东西。我在这里做错了什么,还是有更好的方法来做到这一点?

标签: bashssh

解决方案


问题

"..."执行以下操作时不会评估引号 ( ):

$($COMMAND)

远程 Bash 看到的是两个参数:"echo'hello'"echo 'hello'与您预期的不同。

建议的解决方案:数组

在您的情况下,我建议使用数组:

COMMAND=( ssh localhost "echo 'hello'" )
OUTPUT=$( "${COMMAND[@]}" )

其中的单词(...)是数组的元素。在这种情况下,echo 'hello'可以看到单个元素:在赋值期间立即评估引号。然后,当你这样做时"${COMMAND[@]}",每个元素都将作为参数传递,而不会被触及。

还有其他几种方法可以解决此问题,例如使用eval以便评估引号,但根据我的经验,使用数组是构建和执行命令的最安全和最简单的方法。使用数组很容易防止外壳注入攻击,防止不必要的扩展,防止有空格时单词被破坏。唯一的缺点是数组并非在每个 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'"拆分。


推荐阅读