这里要理解的是ssh 只是连接它的参数(与$* 相同),并将连接的字符串传递给sh -c。
因此,在以下情况下:
ssh josh-play bash -c 'cd /tmp; pwd'
...ssh 运行:
sh -c 'bash -c cd /tmp; pwd'
...因此,sh 运行:
bash -c cd /tmp
pwd
...而且您可以自己测试,bash -c cd /tmp 并没有做太多有用的事情(它运行的脚本文本仅包含 cd;/tmp 存储为参数,但脚本文本从不阅读它的论点,使其没有实际意义)。此外,一旦bash 退出,我们就会回到父进程sh,其中cd 从未运行过。
传递给你的外壳的语法引号完全丢失了,pwd 不是由你手动触发的bash 调用的(在这种用法中,它只是在没有任何参数的情况下调用cd -- @ 987654336@ 在$1 中,但作为参数传递给cd 的脚本从不取消引用该变量)而是通过ssh 隐式调用的sh。
如果您知道您的远程 sh 是由 bash 或 ksh 提供的 - 一个支持 $'' 扩展的 shell - 您可以对任意 argv 数组执行以下操作(在本例中为 bash、@987654343 @,cd /tmp; pwd):
# 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"
上面的警告是,如果您的参数列表可以包含换行符或隐藏字符,则 bash 或 ksh 中的 printf %q 可以以 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'
# ...and use bash to evaluate that quoted form.
ssh josh-play 'exec bash -s' <<<"$rmt_cmd"