How to avoid double-evaluating commands in ssh / adb shell / etc.
I like to use the following echo-args.sh
script to debug shell
double-evaluation problems:
#!/bin/sh
for arg; do
echo \""$arg"\"
done
You can then push this to an android device:
$ chmod u+x echo-args.sh
$ adb push /data/local/tmp/
And run it like so:
$ ./echo-args.sh 'hello world'
"hello world"
$ adb shell /data/local/tmp/echo-args.sh 'hello world'
"hello"
"world"
And here lies the problem. When we run a command with adb shell
(or ssh
) it
gets double-evaluated - once by our current shell, and once by the shell on the
device. Sometimes this might be what you want. e.g. You might want to evaluated
an environment variable in the context of the remote shell.
But it's really annoying when it's not what you want. I've written a
scripts that invoke adb shell
internally and mostly work, except when I
pass arguments with spaces.
I think I've figured out a robust workaround:
run-escaped() {
shell=$1
shift
$shell "$(printf " %q" "$@")"
}
Usage:
$ run-escaped 'adb shell' /data/local/tmp/echo-args.sh 'hello world'
"hello world"
It also works with ssh
:
$ run-escaped 'ssh remotebox' echo-args.sh 'hello world'
"hello world"
This works by first escaping all of the arguments, after they've been evaluated once by the local shell. Then it passes this as a string to the remote shell.
The double-evaluation still happens, but since we escaped our parameters, it's as if it didn't happen.
If you enjoyed this post, please let me know on Twitter or Bluesky.
Posted November 22, 2023.
Tags: #shell