executing remote command / ssh / extra escaping
If you use ssh
to run commands remotely, you may have run into the
problem that you need an extra layer of escaping.
Let’s say you have application myapp
that for some reason only runs on
host myserver
. If you have functional ssh keys to log onto myserver
it can be helpful to create a myapp
wrapper on your desktop. After
all, this:
$ myapp myargs
… is far more convenient than doing this:
$ ssh myserver
$ myapp myargs
$ logout
(Especially if you want to do stuff with stdin
and stdout
.)
The naive approach to /usr/local/bin/myapp
is this:
#!/bin/sh
ssh myserver $*
This has a number of problems:
- The arguments can interpreted as
ssh
options if they begin with a-
(hyphen). Thessh
client I’m dealing with right now plays nice, but it’s not a bad idea to add--
before$*
. $*
is expanded immediately, so every argument with spaces in it is broken up. This needs fixing. And no, passing"$@"
does not help. You’re right in that now arguments with spaces are passed tossh
as a single argument, but it still needs an extra level of escaping (*).- The remote end thinks that there is no tty on the other end, causing
password inputs to break, among other things. If you need a tty, you
can fix this by adding
-t
to the list ofssh
options, the drawback being that output fromstdout
andstderr
is now combined intostdout
.
The proper solution (add -t
only if needed):
#!/bin/sh
args=""
for arg in "$@"; do args="$args '`echo "$arg" | sed -e "s/'/'\\\\\\\\''/g"`'"; done
ssh myserver -- myapp $args
Yes, it looks like a kludge. But it forces single quotes around all your arguments and works like a charm.
(*) The extra expansion is needed to get things like
ssh myserver -- rm '*.png'
to work.