Issue
While debugging a script that runs various commands remotely, I noticed some problems getting output from echo
.
I realize that the bash -c
isn't necessary here, but it still has me wondering.
In my shell:
> bash -c "echo hello && echo hi"
hello
hi
But, if I bring SSH into the picture:
> ssh ${myhost} bash -c "echo hello && echo hi"
hi
Yet, date
outputs, even though that first echo
didn't:
> ssh ${myhost} bash -c "date && echo hi"
Thu Jun 3 21:15:26 UTC 2021
hi
What's going on here?
Solution
When you run a command via ssh
like this, it's parsed twice: first on the local computer (before it's passed to the ssh
command as arguments), then again on the remote computer before it's actually executed. Each time it's parsed, the shell will apply and remove quotes and escapes. That means the double-quotes you have around the command get applied and removed by the local shell, before the command is sent to the remote shell. So what looks like this command:
bash -c "echo hello && echo hi"
Turns into this by the time the remote shell sees it:
bash -c echo hello && echo hi
...which is two separate commands, bash -c echo hello
and echo hi
. The second one, echo hi
, works as you expect, but the first may not.
With bash -c
, the argument immediately after that is taken as the command string to execute, and any further arguments are assigned to $0
, $1
, etc as it runs. So bash -c echo hello
just runs echo
with $0
set to "hello". So it prints a blank line.
If you want the command to be executed as you expect, you need two layers of quotes and/or escapes, one to be applied and removed by the local shell and another to be applied and removed by the remote shell. Any of these will work:
# Single-quotes for local shell, double for remote
ssh ${myhost} 'bash -c "echo hello && echo hi"'
# Double-quotes for local shell, single for remote
ssh ${myhost} "bash -c 'echo hello && echo hi'"
# Double-quotes for local shell, escaped doubles for remote
ssh ${myhost} "bash -c \"echo hello && echo hi\""
# Single-quotes for local shell, escaped characters for remote
ssh ${myhost} 'bash -c echo\ hello\ \&\&\ echo\ hi'
...and many more possibilities. Note that if the command string contains anything like variable references or command substitutions, you need to pay attention to whether you want them to expand on the local or remote computer, and make sure the quoting/escaping method you use accomplishes that.
BTW, in this case since you're running a command with bash -c
, there's actually a third layer of parsing done by the shell invoked by bash -c
. If that command has anything that needed quoting/escaping, keeping the levels straight will be even more complex.
Answered By - Gordon Davisson