1

Assume I have a script with the following contents located at ~/echo.sh on a remote server example.com:

#!/usr/bin/env bash
echo "Arg 1: $1"
echo "Arg 2: $2"
echo "Arg 3: $3"
echo "Arg 4: $4"

Now, on my local machine, I have another script, remote-echo.sh with the following contents:

#!/usr/bin/env bash
ssh example.com "~/echo.sh $*"

The idea is that a user should be able to run remote-echo.sh with any arguments and have those arguments be forwarded to ~/echo.sh on the remote server.

Unfortunately, this doesn't work:

$ ./remote-echo.sh "arg with space" "argwith\"quote" "arg3"
bash: -c: line 0: unexpected EOF while looking for matching `"'
bash: -c: line 1: syntax error: unexpected end of file

How do I fix this? I already tried using $@ instead of $*, but that didn't have any effect on the result at all.

that other guy
  • 116,971
  • 11
  • 170
  • 194
Ajedi32
  • 45,670
  • 22
  • 127
  • 172
  • I believe this question is already in the knowledgebase, btw, with the same (`printf '%q '`) answer... and some other, more portable answers (as bash's `printf %q` isn't guaranteed to generate output that's valid with `/bin/sh`). – Charles Duffy Jul 26 '18 at 22:06
  • ...for that reason, I've sometimes been known to use Python's `pipes.quote()` function (in Python 2, `shlex.quote()` in Python 3) instead, when uncertain which shell would be in use on the remote end. – Charles Duffy Jul 26 '18 at 22:09
  • ...of the linked duplicates, https://stackoverflow.com/questions/45308395/how-to-have-simple-and-double-quotes-in-a-scripted-ssh-command in particular covers that case. – Charles Duffy Jul 26 '18 at 22:11

1 Answers1

4

You need to ensure that all arguments embedded in the string passed to SSH are being properly escaped, so they don't get interpreted by the shell on the remote server. The simplest way to do this is using the %q format character for printf in Bash:

#!/usr/bin/env bash
ssh example.com "~/echo.sh $(printf "%q " "$@")"

According to the help information for printf in Bash:

In addition to the standard format specifications described in printf(1), printf interprets:

[...]

%q quote the argument in a way that can be reused as shell input

That should resolve the issue you see here, and ensure each argument is passed correctly to echo.sh.

Ajedi32
  • 45,670
  • 22
  • 127
  • 172
  • 2
    Notice that the `printf` you use is probably the shell built-in, i.e., the man page would not be relevant - `help printf` would be the documentation to look at. If I remember correctly, the built-in `printf` introduced `%q` before the one in the Coreutils did. – Benjamin W. Jul 26 '18 at 21:35
  • 1
    The word "proposed" there is also rather key. `$''` is **not** presently part of the POSIX sh standard, and common versions of `/bin/sh` (including dash) do not presently support it. – Charles Duffy Jul 26 '18 at 22:13
  • Modern addendum: `"${*@Q}"` is a considerably faster thing than a subshell running printf in bash 5.0+ – Charles Duffy Jul 11 '23 at 16:38
  • Backwards-compatible addendum: `printf -v cmd_q '%q ' "$@"; ssh example.com "~/echo.sh $cmd_q"` avoids the subshell overhead even in pre-5.0 bash. – Charles Duffy Jul 11 '23 at 16:39