I need pass $var_path to bash script inside single quotes and then get commnd executed on remote host. I know that single quotes do not allow passing variables, so tried double quoting, but getting error in sed. Assume this happens because its template uses " as well.
var="Test text"
var_path="/etc/file.txt"
echo "$var"|ssh root@$host 'cat - > /tmp/test.tmp && sed -n "/]/{:a;n;/}/b;p;ba}" $var_path > /tmp/new.conf.tmp'
so with ""
var="Test text"
var_path="/etc/file.txt"
echo "$var"|ssh root@$host "cat - > /tmp/test.tmp && sed -n "/]/{:a;n;/}/b;p;ba}" $var_path > /tmp/new.conf.tmp"
Errors from sed
sed: -e expression #1, char 0: unmatched `{'
./script.sh: line 4: n: command not found
./script.sh: line 4: /}/b: No such file or directory
./script.sh: line 4: p: command not found
If $var_path used directly without substitution script works as expected.
Once you use double quotes the embedded command can simply use single quotes. This works because double quote variable substitutions treat embedded single quotes as regular characters... nothing special.
If you wanted to assign and use variables within the double quotes that would be more tricky.
root@anywhere is dangerous... is there no other way? If you are using certificates I hope they restrict root to specific commands.
Arguments parsed as part of the command to send to the remote system in SSH in the are concatenated with spaces and then passed to the remote shell (in a manner similar to
"${SHELL:-sh}" -c "$*"
). Fortunately, bash has the built-inprintf %q
operation (an extension, so not available in all other POSIX shells) to make strings eval-safe, and thus ssh-safe, if your remoteSHELL
is bash; see the end of this answer for a workaround using Python'sshlex
module to generate a command safe in non-bash shells.So, if you have a command that works locally, the first step is to put it into an array (notice also the quotes around the expansion of
"$var_path"
-- these are necessary to have an unambiguous grouping):...which you can run locally to test:
...or transform into an eval-safe string:
...and then run it locally with ssh...
...or test it locally with
eval
:Now, your specific use case has some exceptions -- things that would need to be quoted or escaped to use them as literal commands, but which can't be quoted if you want them to retain their special meaning.
&&
,|
and>
are examples. Fortunately, you can work around this by building those parts of the string yourself:...which is equivalent to the local array expansion...
...or the local eval...
Addendum: Supporting Non-Bash Remote Shells
One caveat:
printf %q
is guaranteed to quote things in such a way thatbash
(or ksh, if you're usingprintf %q
in ksh locally) will evaluate them to exactly match the input. If you had a target system with a shell which didn't support extensions to POSIX such as$''
, this guarantee would no longer be available, and more interesting techniques become necessary to guarantee robustness in the face of the full range of possible inputs.Fortunately, the Python standard library includes a function to escape arbitrary strings for any POSIX-compliant shell:
Thereafter, when you need an equivalent to
printf '%q ' "$@"
that works even when the remote shell is not bash, you can run: