这些命令如何导致不同的输出?
⋊> ssh host bash -c 'cd /tmp; pwd'
/home/manu
⋊> ssh host "bash -c 'cd /tmp; pwd'"
/tmp
对类似问题here的回答指出:
尾随参数组合成一个字符串,该字符串作为参数传递给远程计算机上登录shell的-c选项。
如果是*
,我可以理解这一点,但在这个例子中,本地评估的部分是什么?此外,bash命令之前的--
没有帮助。
答案 0 :(得分:6)
这里要理解的是ssh
只是简单地连接它的参数(与$*
一样),并将该连接的字符串传递给sh -c
。
因此,在以下情况下:
ssh josh-play bash -c 'cd /tmp; pwd'
... ssh运行:
sh -c 'bash -c cd /tmp; pwd'
...因此,sh run:
bash -c cd /tmp
pwd
...正如您可以自己测试一样,bash -c cd /tmp
并没有做太多有用的事情(它运行的脚本文本只包含cd
; {{1}存储为一个参数,但脚本文本永远不会读取它的参数,这样就没有了。此外,只要/tmp
退出,我们就会回到父bash
进程中,sh
从未运行过。
传递给外壳的语法引号完全丢失,cd
不是由您手动触发的pwd
调用的(在此用法中,只调用bash
没有任何参数 - cd
在/tmp
中,但作为参数传递给$1
的脚本从不取消引用该变量),而是由ssh隐式调用的cd
。 / p>
如果您知道您的远程sh
由bash或ksh提供 - 一个支持sh
扩展名的shell - 您可以对任意argv数组执行以下操作(在本例中为{{ 1}},$''
,bash
):
-c
上面的警告是,如果您的参数列表可以包含换行符或隐藏字符,bash或ksh中的cd /tmp; pwd
可以以POSIX sh无法读取的形式转义它。为了避免这种情况:
# ask your shell to generate an eval-safe quoted form of your argument list
printf -v rmt_cmd '%q ' bash -c 'cd /tmp; pwd'
# pass that through to be run by the remote sh -c
ssh josh-play "$rmt_cmd"