0

在本地运行与通过 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 是否以不同的方式处理命令参数并以某种方式删除转义字符?(或者本地外壳本身正在这样做?)

4

1 回答 1

2

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 或类似方式访问),然后告诉远程系统将该文件作为脚本运行。

于 2021-05-05T10:03:25.470 回答