3 ms·
Yep! God though, this hits me in the face so often. Trying to add `sh -c` to fix it is a trap, because obviously, you just create yet another layer of escaping.
by jchw 3y ago
Yep! God though, this hits me in the face so often. Trying to add `sh -c` to fix it is a trap, because obviously, you just create yet another layer of escaping.
It really becomes one hell of a puzzle sometimes, especially if you're necessarily nesting another layer of escaping. It feels like you're trying to write a quine.
This works:
ssh host -- ls "folder\ name"
This also works:
ssh host -- ls \"folder name\"
This works:
ssh host -- sh -c \"ls \\\"folder name\\\"\"
OK, so clearly, just throwing more escaping at it fixes it. But even if you figure that out, the real mental gymnastics would be figuring out which of the three shells interpreting your command line in the last case would handle shell expansion.
In this case, it's the host:
ssh host -- sh -c \"ls \\\"folder nam\\\"*\"
In this case it's the remote:
ssh host -- sh -c "\"ls \\\"folder nam\\\"*\""
Of course where you put the quotes makes no difference. All it does is prevent your shell from processing it. So this works just as well:
ssh host -- "sh -c \"ls \\\"folder nam\\\"*\""
If you sit and think each layer through, it usually isn't completely impossible to understand, but the odds that you are going to get something wrong the first time is astonishingly high.
It does make me wonder why ssh handles it the way it does, though. Because with the way SSH handles it, it may as well just automatically escape the spaces. Right now, not putting an SSH command in quotes doesn't make much sense unless you for some reason want local shell expansion for something.
- brazzledazzle 3y agoOnce I run into nested escape wrangling I start to seriously question how I'm trying to accomplish something.
- josephg 3y agoOnce I run into nested escape wrangling I seriously question the sanity of my tools.
- lanstin 3y agoIf I can't fix it in like 3 tries, I switch to my local shell script having the script to run on the remote end be in a HEREDOC and just scp it it over and then ssh exec it. Inside the HEREDOC, it's a sane environment.
- theK 3y agoCopying over the commands to run also has another added benefit, you have quite good documentation of what was run and when. Also it allows one to easily run a failing thing again (in case output gets mangled in the executing script)
- lanstin 3y agoAnd idempotency is essential for shell scripts that alter state. Otherwise you have to keep track of where failures happened.
- septune 3y agowrap it in base64 then cat it | ssh « base64 -d | bash »
- Arnavion 3y ago`printf '%q'` is your friend.
- Wicher 3y agoIf you can invest a little bit of time in configuration of the remote host, then my "special shell" (also mentioned elsewhere on this page) may be of use, and you will no longer need to hit yourself in the face so often! mention: https://news.ycombinator.com/item?id=36726111 https://news.ycombinator.com/item?id=36726111 crate: https://crates.io/crates/arghsh https://crates.io/crates/arghsh
- zokier 3y agoneat. one little pedantic note, argsh requires all arguments to be valid unicode strings while I think at least Linux allows argv to be arbitrary sequences of non-zero bytes. But then I really hope there are not many cases where that is relevant consideration.
- Wicher 3y agoGood point, I'll add that to the README next time I touch it. To warn off those who were thinking about passing jpegs via argv ;-)
- mananaysiempre 3y ago> This works: ssh host -- ls "folder\ name" > This also works: ssh host -- ls \"folder name\" Uh, why not ssh host -- ls '"folder name"' ? Single quotes are the shell’s ultimate bulk “no touchy” escape, so if you don’t need them in the inner command, it seems easier to use them for everything. (Also when passing programs to sed, awk, jq, xmlstarlet, etc.)
- jchw 3y agoI believe that also works, but I don't do it often because I often wind up wanting to be able to use variables from the host, especially e.g. in cron jobs and scripts.
- mananaysiempre 3y agoYes, that’s a valid thing to want. At that point, though, I’d try to write a general shell escaping function, because I don’t trust myself to figure out which host things are OK to include unescaped in such a situation. (Here’s when I start to long for Tcl, despite all the times I’ve had to spell out [string index ...].) The optimal solution would be to have a separate side channel for passing things to the quoted program, like -v in awk or --arg in jq (or whatever it is in your favourite SQL DBMS binding) but I don’t think SSH will let you do that.
- nneonneo 3y agoIf you do need single quotes in the inner command, you can use the "'" trick: ssh host -- ls "'"'folder$name'"'"
- wolletd 3y agoFrom my experience, it's possible to just do ssh host 'ls "folder name"' and practically ignore the `command [arguments]` split in SSHs synopsis. It's all passed to a shell and parsed again, anyway.
- 1vuio0pswjnm7 3y agoFor simple tasks, that's how I have always done it. Alternatively, echo ls \"folder name\"|ssh -T host or cat > 1 ls "folder name" ^D ssh -T host < 1
- argulane 3y agoThe last example really looks like perfect case for using `here document` feature. ssh -T host <<EOF ls "folder name" EOF