我试图通过 ssh 1-liner 调用在远程机器上运行几个命令,方法是将它们指定为传递给“bash -c”的以分号分隔的字符串。它适用于某些情况,但不适用于其他情况。看一下这个:
# Note: the "echo 1" output is lost:
bash-3.2$ ssh sandbox bash -c "echo 1; echo 2; echo 3"
2
3
# Note: first echo is ignored again
bash-3.2$ ssh sandbox bash -c "echo 0; echo 1; echo 2; echo 3"
1
2
3
# But when we run other commands (for example "date") then nothing is lost
bash-3.2$ ssh sandbox bash -c "date; date;"
Wed Nov 7 20:27:55 UTC 3018
Wed Nov 7 20:27:55 UTC 3018
Run Code Online (Sandbox Code Playgroud)
我错过了什么?
远程操作系统:Ubuntu 16.04.5 LTS
远程 ssh:OpenSSH_7.2p2 Ubuntu-4ubuntu2.4、OpenSSL 1.0.2g 2016 年 3 月 1 日本地
操作系统:macOS High Sierra Versoin 10.13.3
本地 ssh:OpenSSH_7.6p1、LibreSSL 2.6.2
更新:上面的例子是我正在尝试做的事情的非常简化的图片。实际应用实际上是通过回显到远程文件系统来在远程框上生成一些文件:
#!/bin/bash
A=a
B=b
C=c
ssh -i ~/.ssh/${REMOTE_FQDN}.pem ${REMOTE_FQDN} sudo bash -c \
"echo $A > /tmp/_a; echo $B > /tmp/_b; echo $C > /tmp/_c;"
Run Code Online (Sandbox Code Playgroud)
运行上述脚本并转到远程框检查结果后,我看到以下内容:
root@sandbox:/tmp# for i in `find ./ -name '_*'|sort`; do echo "----- ${i} ----"; cat $i; done
----- ./_a ----
----- ./_b ----
b
----- ./_c ----
c
Run Code Online (Sandbox Code Playgroud)
如您所见,第一个“echo”命令生成了空白文件!
需要明确的是,这里有 3 个外壳在工作 - 一个解释 ssh 的外壳,即你的本地外壳;ssh
将自动为您运行的那个,并且bash
您正在显式调用。
1“消失”的原因是解释ssh
命令的shell “吃掉”了-c
参数周围的引号,然后另一侧的shellssh
在空格处拆分参数。所以它最终看起来像bash -c echo 1 ; echo 2; echo 3
. 反过来,-c
只是 gets echo
,它与一个空行相呼应;1
成为那个 shell 的值$1
,它没有被使用。然后内部bash
返回,直接ssh
shellecho 2; echo 3
正常运行。
考虑一下:
$ ssh xxx bash -c "'echo 1'; echo 2; echo 3"
1
2
3
Run Code Online (Sandbox Code Playgroud)
whereecho 1
在 ssh 参数中受到保护,因此传递了第二级 ssh shell bash -c 'echo 1'; echo 2; echo 3
。最里面的第 3 级 shell 回显 1,然后第 2 级 ssh shell 回显 2 和 3。
这是另一个有趣的排列:
$ ssh xxx bash -c "'echo 1; echo 2; echo 3'"
1
2
3
Run Code Online (Sandbox Code Playgroud)
在这里,内壳获得所有回声,因为它们被分组在第一个壳内"
和第二个壳内'
。
通常,将参数传递给运行 shell 脚本的 shell 脚本的 shell 脚本可能很难构建。我建议您稍微改变一下技术以节省大量精力。不是将 shell 命令作为命令行参数传递给ssh
参数,而是通过标准输入将其提供给 shell。考虑使用这样的管道,避免递归 shell 解释:
$ echo "echo 1; echo 2; echo 3" | ssh -T xxx
1
2
3
Run Code Online (Sandbox Code Playgroud)
(在这里,这-T
只是为了抑制 ssh 抱怨缺少伪终端)。