https://www.chiark.greenend.org.uk/~cjwatson/blog/ssh-quoting.html Colin Watson's blog * debian * grub * launchpad * libpipeline * man-db * misc * summerofcode * ubuntu SSH quoting Tweet 2021-06-11 by Colin Watson * debian * openssh planet-debian planet-ubuntu 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 user@machine.local 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]: user@machine.local [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 user@machine.local 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 user@machine.local 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 user@machine.local '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 user@machine.local 'bash -lc "cd /tmp;pwd"' Shell parsing is hard. social * atom feed * Twitter * Google+ Powered by Pelican. The theme is based on one by Smashing Magazine, thanks!