<span class="caps">SSH</span> quoting | Colin Watson's blo...
source link: https://www.chiark.greenend.org.uk/~cjwatson/blog/ssh-quoting.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Jun 11 2021 SSH quoting
A while back there was a thread on one of our company mailing lists about SSH quoting, and I posted a long answer to it. Since then a few people have asked me questions that caused me to reach for it, so I thought it might be helpful if I were to anonymize the original question and post my answer here.
The question was why a sequence of commands involving ssh
and fiddly
quoting produced the output they did. The first example was this:
$ ssh [email protected] bash -lc "cd /tmp;pwd" /home/user
Oh hi, my dubious life choices have been such that this is my specialist subject!
This is because SSH command-line parsing is not quite what you expect.
First, recall that your local shell will apply its usual parsing, and the
actual OS-level execution of ssh
will be like this:
[0]: ssh [1]: [email protected] [2]: bash [3]: -lc [4]: cd /tmp;pwd
Now, the SSH wire protocol only takes a single string as the command, with the expectation that it should be passed to a shell by the remote end. The OpenSSH client deals with this by taking all its arguments after things like options and the target, which in this case are:
[0]: bash [1]: -lc [2]: cd /tmp;pwd
It then joins them with a single space:
bash -lc cd /tmp;pwd
This is passed as a string to the server, which then passes that entire string to a shell for evaluation, so as if you’d typed this directly on the server:
sh -c 'bash -lc cd /tmp;pwd'
The shell then parses this as two commands:
bash -lc cd /tmp pwd
The directory change thus happens in a subshell (actually it doesn’t quite
even do that, because bash -lc cd /tmp
in fact ends up just calling cd
because of the way bash -c
parses multiple arguments), and then that
subshell exits, then pwd
is called in the outer shell which still has the
original working directory.
The second example was this:
$ ssh [email protected] bash -lc "pwd;cd /tmp;pwd" /home/user /tmp
Following the logic above, this ends up as if you’d run this on the server:
sh -c 'bash -lc pwd; cd /tmp; pwd'
The third example was this:
$ ssh [email protected] bash -lc "cd /tmp;cd /tmp;pwd" /tmp
And this is as if you’d run:
sh -c 'bash -lc cd /tmp; cd /tmp; pwd'
Now, I wouldn’t have implemented the SSH client this way, because I agree
that it’s confusing. But /usr/bin/ssh
is used as a transport for other
things so much that changing its behaviour now would be enormously
disruptive, so it’s probably impossible to fix. (I have occasionally
agitated on openssh-unix-dev@ for at least documenting this better, but
haven’t made much headway yet; I need to get round to preparing a
documentation patch.) Once you know about it you can use the proper
quoting, though. In this case that would simply be:
ssh [email protected] 'cd /tmp;pwd'
Or if you do need to specifically invoke bash -l
there for some reason
(I’m assuming that the original example was reduced from something more
complicated), then you can minimise your confusion by passing the whole
thing as a single string in the form you want the remote sh -c
to see, in
a way that ensures that the quotes are preserved and sent to the server
rather than being removed by your local shell:
ssh [email protected] 'bash -lc "cd /tmp;pwd"'
Shell parsing is hard.
Posted by Colin Watson on 2021-06-11 in debian. Tags: openssh, planet-debian, planet-ubuntu.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK