当命令在本地(/bin/sh -c CMD)与远程(ssh node1 /bin/sh -c CMD)获得 运行 时,shell(sh 或 csh)处理参数的行为是否不同?

Does shell (sh or csh) behave differently for handling arguments when commands get run locally(/bin/sh -c CMD) vs remotely (ssh node1 /bin/sh -c CMD)?

当 运行 在本地与通过 ssh 远程 运行 时,我得到以下命令的不同行为。

$ /bin/csh -c \'/bin/echo a b c d\'
Unmatched '''.
$ ssh node1 /bin/csh -c \'/bin/echo a b c d\'
a b c d

这里的 ssh 是否以不同方式处理命令参数并以某种方式删除转义字符? (或者本地 shell 本身正在这样做?)

TLDR:您的示例因本地 shell 处理而失败。

在 Unix 上,当进程 x(例如 shell)运行 是一个不同的程序 y(通常作为子进程,但不一定)它传递 y 自己的名称 加上任何参数作为字符串的可变长度数组,例如在旧系统上 echo a b c d 运行s echo 带有参数数组的程序(为清楚起见每行显示一个)

echo
a
b
c
d

(在最近的系统 echo 上的大多数 shell 是 shell 内置的而不是一个单独的程序,但是 其他 程序仍然有效传统方式。)

因为 C 数组从下标 0 开始并且参数数组通常命名为 argv(参数向量——C 派生自 BCPL,其中数组被称为向量)C(和 C++ 等)程序员经常调用程序名称“argv-zero”。请注意,您可以让同一个程序在文件系统中以多个名称出现,因此它的名称在编译时是未知的,只有当它是 运行 时才知道;这个技巧曾经在gzip/gunzip之类的事情上很流行,但最近几十年似乎变得不流行了。

在shell中输入反斜杠单引号意味着将该单引号视为普通数据,而不是用它来引用其他字符,例如space分隔单词和参数,所以

/bin/csh -c \'/bin/echo a b c d\'

运行s /bin/csh 具有以下 argv:

/bin/csh
-c
'/bin/echo
a
b
c
d'

csh(或 Bourne 类型 shell)在 -c 之后接受 一个 参数作为整个命令 line(或脚本)进行解析并(可能)执行结果,但 '/bin/echo 不是有效的命令行。任何剩余的参数都不会被视为命令,而是可能的参数 命令 运行 如果需要它们。

另一方面,ssh 客户端程序将任何和所有 'data' 参数(不是选项,也不是必需的 [user@]remotehost 参数)视为构成命令的 运行 远程,并通过 SSH 协议将它们全部连接成 一行 ,因此您的 ssh 命令得到

ssh
node1
/bin/csh
-c
'/bin/echo
a
b
c
d'

但发送

/bin/csh -c '/bin/echo a b c d'
远程系统上的

sshd 将整行作为一个参数传递给您的登录 shell,然后 运行 s csh

/bin/csh
-c
/bin/echo a b c d

所以它把-c后面的一个参数作为命令行,它可以解析成功并且运行。

这种组合就是为什么我们在几个堆栈上有几十个问题,用于“如何使用特殊字符 and/or 插值正确地使用 ssh 远程 运行 此命令”的许多变体。虽然通常有一个可能的答案,但通常最容易的方法是通过 运行ning ssh 不带远程命令参数,因此它远程 运行s 是一个交互式 shell,并且将所需的命令作为 input 发送到那个 shell;或者,几乎相同,将命令放入文件中,将该文件传输到远程系统(或使其可通过 NFS 或类似方式访问),然后告诉远程系统 运行 file 作为脚本。