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?
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
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"