为什么带有引号的空格的命令在通过 ssh 传递时不起作用?

Sud*_*ura 3 unix ssh bash quotes escaping

我正在构建这个项目,其中我必须使用 ssh 执行 UNIX 命令。这是我的问题:当我在 UNIX shell 中执行以下命令时,

echo "Hello           World"
Run Code Online (Sandbox Code Playgroud)

它产生输出:

echo "Hello           World"
Run Code Online (Sandbox Code Playgroud)

中间有 5 个空格,因为参数是在双引号内给出的。我也想用 ssh 达到同样的效果。我试图使用反斜杠转义双引号,如下所示:

ssh localhost echo \"Hello           World\"
Run Code Online (Sandbox Code Playgroud)

因为我不想让 bash 处理引号。但它只是产生

Hello           World
Run Code Online (Sandbox Code Playgroud)

“Hello”后面只有一个空格,这是为什么?

Cha*_*ffy 5

ssh执行通过调用远程 shell 给出的命令,其中脚本由完整的本地参数集组成,参数之间用空格连接在一起。


如果你运行:

# This has only syntactic quotes -- none of the quotes are literal.
ssh somehost echo "Hello   World"
Run Code Online (Sandbox Code Playgroud)

..那么传递给的参数列表ssh(在 C 语法中,不包括主机名和 ssh 命令本身)是{ "echo", "Hello World", NULL }

连接在一起形成一个字符串,然后就变成了"echo Hello World"

因此,远程 shell 运行echo Hello World,它没有语法引号。


如果你运行:

ssh somehost echo \"Hello   World\"
Run Code Online (Sandbox Code Playgroud)

...然后命令列表(同样是 C 语法)是{ "echo", "\"Hello", "World\"", NULL }: 引号是文字,但空格在分词过程中被本地 shell(在运行 SSH 之前)删除。

连接成字符串,就变成了echo "Hello World"。因此,您得到了引号,但失去了空格。


相比之下,作为一个有效(但不是特别最佳实践)的示例,请考虑:

ssh somehost echo \"Hello"   "World\"
Run Code Online (Sandbox Code Playgroud)

在这里,您传递ssh两个参数:\"开头和结尾的 s 对于第一个 shell 来说是字面意思(没有语法意义,因此该单词Hello对于第一个 shell 而言不带引号);但是"围绕空格的 s 是语法上的,它告诉 shell 在调用时保留该空格(因此空格引用)ssh。因此,您得到{ "echo", "\"Hello World\"", NULL },它连接到字符串echo "Hello World"


最佳实践是通过仅将包含要运行的整个脚本的单个字符串传递到 ssh 来避免此行为。

# This works
ssh somehost 'echo "Hello   World"'
Run Code Online (Sandbox Code Playgroud)

...如果您想以编程方式生成该字符串,您可以这样做(如果您知道您的远程 shell 是 bash - 即使 bash-in-/bin/sh模式也是安全的):

remote_command=( echo "Hello   World" )
printf -v remote_command_q '%q ' "${remote_command[@]}"
ssh somehost "$remote_command_q"
Run Code Online (Sandbox Code Playgroud)

要使用任何符合 POSIX 标准的远程 shell 安全地执行此操作,请参阅如何在脚本化 ssh 命令中使用简单引号和双引号中的 Python 辅助答案