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
sshoptions if they begin with a-(hyphen). Thesshclient 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 tosshas 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
-tto the list ofsshoptions, the drawback being that output fromstdoutandstderris 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.