Why doesn't a command with quoted whitespace w

2019-05-31 13:49发布

问题:

I am building this project in which I have to execute UNIX commands using ssh. Here is my question : When I execute the following command in a UNIX shell,

echo "Hello           World"

It produces the output :

Hello           World

With 5 spaces in between as the arguments were given within double quotes. I want to achieve the same effects with ssh as well. I am trying to escape the double quotes using backslash as follows :

ssh localhost echo \"Hello           World\"

as I do not want bash to process the quotes. But it just produces

Hello           World

There is only one space after "Hello" Why is that?

回答1:

ssh executes a command it's given by invoking a remote shell with a script composed of the full set of local arguments concatenated together with whitespace between them.


If you run:

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

..then this list of arguments passed to ssh (in C syntax, excluding the hostname and the ssh command itself) is { "echo", "Hello World", NULL }.

Concatenated together into a string, this then becomes "echo Hello World".

Thus, the remote shell runs echo Hello World, which has no syntactic quotes.


If you run:

ssh somehost echo \"Hello   World\"

...then the list of commands (again, in C syntax) is { "echo", "\"Hello", "World\"", NULL }: The quotes are literal, but the spaces were removed by the local shell (before SSH was run) during the word-splitting process.

Concatenated into a string, this becomes echo "Hello World". Thus, you get quotes, but lose your spaces.


By contrast, as a working (but not particularly best-practice) example, consider:

ssh somehost echo \"Hello"   "World\"

Here, you're passing ssh two arguments: The \"s at the beginning and end are literal to the first shell (having no syntactical meaning, so the word Hello is to that first shell unquoted); but the "s just surrounding the whitespace is syntactical, and tells the shell to preserve that whitespace (so the spaces are quoted) when invoking ssh. Thus, you get { "echo", "\"Hello World\"", NULL }, which concatenates to the string echo "Hello World".


The best practice is to avoid this behavior by passing only a single string to ssh containing the entire script you want to run.

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

...if you want to generate that string programatically, you can do so as such (if you know that your remote shell is bash -- even bash-in-/bin/sh mode is safe):

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

To do that safely with any POSIX-compliant remote shell, see the Python-assisted answer in How to have simple and double quotes in a scripted ssh command



回答2:

The quotes are present only on the client, and mean that the server sees this:

echo Hello     World

But the quotes are already lost at this point, so that whitespace is collapsed.

You can use single quotes around the whole command:

ssh localhost 'echo "Hello     World"'

Now the local shell does not process the string, so the inner quotes remain intact.


The case with the escaped quotes means that the quotes are ineffective on the local shell, so the whitespace is collapsed locally, then the server sees:

echo "Hello World"