在本地运行与通过 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 是否以不同的方式处理命令参数并以某种方式删除转义字符?(或者本地外壳本身正在这样做?)
TLDR:您的示例由于本地外壳处理而失败。
在 Unix 上,当进程 x(例如 shell)运行不同的程序 y(通常作为子进程,但不一定)时,它将 y 自己的名称加上任何参数作为可变长度的字符串数组传递,例如在旧系统echo a b c d
上运行echo
带有参数数组的程序(为清楚起见,每行显示一个)
echo
a
b
c
d
(最近系统上的大多数 shellecho
是内置的 shell,而不是单独的程序,但其他程序仍然以传统方式工作。)
因为 C 数组从下标 0 开始,并且参数数组通常命名为argv
(参数向量 - C 派生自 BCPL,其中数组称为向量)C(和 C++ 等)程序员通常将程序名称称为“argv-zero”。请注意,您可以让同一个程序在文件系统中以多个名称出现,因此它的名称在编译时是未知的,只有在运行时才知道;这个技巧曾经很流行,gzip/gunzip
但近几十年来似乎已经过时了。
在 shell 输入中,反斜杠单引号意味着将该单引号视为普通数据,而不是用它来引用其他字符,例如分隔单词和参数的空格,因此
/bin/csh -c \'/bin/echo a b c d\'
/bin/csh
使用以下 argv运行:
/bin/csh
-c
'/bin/echo
a
b
c
d'
csh(或 Bourne 类型的 shell)将一个参数-c
作为要解析的整个命令行'/bin/echo
(或脚本)并(可能)执行,但不是有效的命令行。任何剩余的参数都不被视为命令,而是在需要时作为正在运行的命令的可能参数。
ssh
另一方面,客户端程序将任何和所有“数据”参数(不是选项,也不是必需的 [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
在远程系统上将该整行作为一个参数传递-c
给您的登录外壳,然后将其作为一个参数,前面是一个单独的参数,然后运行 csh
/bin/csh
-c
/bin/echo a b c d
所以它将 -c 之后的一个参数作为命令行,它可以成功解析并运行。
这种组合就是为什么我们在几个堆栈上有几十个 Q,用于“我如何让 ssh 使用特殊字符和/或插值正确地远程运行这个命令”的许多变体。虽然通常有一个可能的答案,但通常最容易通过运行没有远程命令参数的 ssh 来解决问题,因此它远程运行交互式 shell,并将所需的命令作为输入发送到该 shell;或者,几乎相同,将命令放入文件中,将该文件传输到远程系统(或使其通过 NFS 或类似方式访问),然后告诉远程系统将该文件作为脚本运行。