Fish-shell: {x,y,z} parameter expansion is not escaped

Created on 30 Dec 2017  ·  3Comments  ·  Source: fish-shell/fish-shell

Fish parameters are, as a rule, escaped before being injected into fish commands:

mqudsi@freebsd> echo hello > hello\ world.txt
mqudsi@freebsd> file (echo hello world.txt)
hello world.txt: ASCII text

However, the result of braces expansion is not correctly escaped, _and_ there's no easy way to escape to manually escape it as introducing quotes around the parameter breaks the expansion:

mqudsi@freebsd> echo -n hello\ {1,2,3}.txt\n
hello 1.txt
 hello 2.txt
 hello 3.txt

As you can see, after the first parameter, echo is being passed two separate arguments hello and #.txt, leading to the extra space in front of the words.

Wrapping the arguments in quotes leads to the following:

mqudsi@freebsd> echo -n "hello\ {1,2,3}.txt\n"
hello\ {1,2,3}.txt\n

While attempting to work around the issue by inserting escaped quotes into the original string (under the presumption that the contents of the string are not being escaped after replace and before injection) leads to the quotes being correctly escaped, preventing the desired result:

mqudsi@freebsd> echo -n \"hello\ {1,2,3}.txt\n\"                                                                                                     "hello 1.txt                                                                                                                                                       " "hello 2.txt                                                                                                                                                     " "hello 3.txt                                                                                                                                                     
"

It looks like the parameter expansion is happening after the argument splitting stage.

question

All 3 comments

echo is being passed two separate arguments hello and #.txt, leading to the extra space in front of the words.

I don't agree with that diagnosis. echo is being passed 3 arguments (each ending with \n), which echo dutifully prints with a space inbetween. I see no bug here.

Here is what I do to debug arguments:

~> printf '«%s»' hei\ {1,2,3}.txt\n
«hei 1.txt
»«hei 2.txt
»«hei 3.txt
»⏎

Here is what I do to debug arguments:

I use string escape, i.e.

> string escape -- hello\ {1,2,3}.txt\n
hello\ 1.txt\n
hello\ 2.txt\n
hello\ 3.txt\n

which means you actually get what I'd expect. You get the full argument with "1", then with "2" and then with "3". The full argument just happens to include a newline.

@mqudsi: I would assume what's throwing you off here is one of a couple of things:

  • We only split command substitutions ((command)) on newline. That means the \n here has no special meaning after being replaced with a newline character. It's "a character", as far as the rest is concerned. We don't need to _escape_ the character, we just don't run the splitting.

Or in file (echo hello world.txt), the "hello world.txt" _is not_ escaped to hello\ world.txt, it's just not split because there's no newline. If you have a file called hello\nworld.txt, this would have issues similar to bash's problem with spaces.

  • echo inserts spaces between its arguments (if "-s" has not been given). That's what's causing the weird indentation - because the spaces happen right after the newline.

That means if you want to have the output

hello 1.txt
hello 2.txt
hello 3.txt

use echo -s or printf '%s\n' hello\ {1,2,3}.txt.

I believe this is a misunderstanding, rather than a bug. Am I correct?

Sorry, folks! I was trying to come up with a minimal repro for an issue and was confused by something completely different. I got the behavior of parameter expansion wrong, in particular, I confused it with that of parallel or xargs with a limit of 1, i.e. I presumed echo {1,2,3} would expand to 3 commands with 1 argument each and not 1 command with 3 arguments.

Thanks for setting me straight.

Was this page helpful?
0 / 5 - 0 ratings