【问题标题】:Why doesn't a command with quoted whitespace work when passed over ssh?为什么通过 ssh 传递的带有引号空格的命令不起作用?
【发布时间】:2017-09-07 11:39:28
【问题描述】:

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

echo "Hello           World"

它产生输出:

Hello           World

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

ssh localhost echo \"Hello           World\"

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

Hello           World

“你好”后面只有一个空格 这是为什么呢?

【问题讨论】:

    标签: bash unix ssh escaping quotes


    【解决方案1】:

    ssh 通过调用远程 shell 来执行它给出的命令,该脚本由一组完整的本地参数和它们之间的空格连接在一起。


    如果你运行:

    # This has only syntactic quotes -- none of the quotes are literal.
    ssh somehost echo "Hello   World"
    

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

    连接在一起成为一个字符串,然后变成"echo Hello World"。

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


    如果你运行:

    ssh somehost echo \"Hello   World\"
    

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

    连接成一个字符串,变成echo "Hello World"。因此,您会得到引号,但会丢失空格。


    相比之下,作为一个可行的(但不是特别好的实践)示例,请考虑:

    ssh somehost echo \"Hello"   "World\"
    

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


    最好的做法是通过只向 ssh 传递一个包含您要运行的整个脚本的字符串来避免这种行为。

    # This works
    ssh somehost 'echo "Hello   World"'
    

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

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

    要使用任何符合 POSIX 的远程 shell 安全地执行此操作,请参阅How to have simple and double quotes in a scripted ssh command 中的 Python 辅助答案

    【讨论】:

    • 当我以下列方式转义双引号时会发生什么? ssh somehost echo \"Hello World\" 那么我不只是有 2 个参数。我有 3 个。这 3 个将连接为“echo \"Hello World\"”。如果 ssh 运行该命令,它不应该打印“Hello world”吗?
    • ...已编辑以将我的 cmets 修改为答案,因此我将它们从历史记录中清除。
    • @SudhaSompura,...我错过了一个问题吗?将echo "Hello World" 传递给远程shell(在使用转义保留本地端的引号之后),引号(在远程端)被视为语法;它们用于在将参数向量传递给echo之前指导远程shell进行字符串拆分,因此echo得到的字符串是Hello World(而不是两个单独的参数,Hello和World , 就像没有语法引号的情况一样)。
    • 关于“程序化”的方式,数组的思想能不能应用到更复杂的例子上,比如多语句的例子?
    • @TomFenech,通过 ssh 可靠地传递多个语句的最简单方法是将它们封装在一个函数中。 myfunc() { echo "Hello World"; echo " * Goodbye World * "; },然后你可以ssh somehost "$(declare -f myfunc); myfunc",你的函数中的所有引用/转义/等都会正确表示。
    【解决方案2】:

    引号仅出现在客户端上,表示服务器可以看到:

    echo Hello     World
    

    但此时引号已经丢失,因此空格被折叠了。

    您可以在整个命令周围使用单引号:

    ssh localhost 'echo "Hello     World"'
    

    现在本地shell不处理字符串,所以内引号保持不变。


    有转义引号的情况表示引号在本地shell无效,所以本地折叠空格,然后服务器看到:

    echo "Hello World"
    

    【讨论】:

    • 但是我已经转义了双引号,对吧?它不能删除双引号。
    • 它不会删除引号,但空格已经丢失。我编辑解释。
    • @SudhaSompura,请记住,有两个外壳,而不是一个:有本地外壳,然后是另一端运行的外壳。因此,您需要有 两层 层引用。
    猜你喜欢
    • 2020-09-13
    • 1970-01-01
    • 2016-07-25
    • 1970-01-01
    • 2015-05-13
    • 2012-09-06
    • 1970-01-01
    • 1970-01-01
    • 2023-03-13
    相关资源
    最近更新 更多