zsh

Why are quotes not removed during expansion in zsh, as stated in the docs?


Consider the below snippet, which does a search with fdfind.

opts='--hidden --no-ignore --exclude ".git"'
fdfind ${=opts}

(note the equality sign, which turns on word splitting in zsh so it behaves like sh).

That snippet does not exclude .git from the search. Why is that?

Relevant parts of the docs (I added bold):

14 Expansion

Process Substitution Parameter Expansion Command Substitution Arithmetic Expansion Brace Expansion

These five are performed in left-to-right fashion. On each argument, any of the five steps that are needed are performed one after the other. Hence, for example, all the parts of parameter expansion are completed before command substitution is started. After these expansions, all unquoted occurrences of the characters ‘\’,‘’’ and ‘"’ are removed.

6.9 Quoting

A character may be quoted (that is, made to stand for itself) by preceding it with a ‘\’. ‘\’ followed by a newline is ignored.

A string enclosed between ‘$’’ and ‘’’ is processed the same way as the string arguments of the print builtin, and the resulting string is considered to be entirely quoted. A literal ‘’’ character can be included in the string by using the ‘\’’ escape.

...

All characters enclosed between a pair of single quotes (’’) that is not preceded by a ‘$’ are quoted. ... Inside double quotes (""), parameter and command substitution occur, and ‘\’ quotes the characters ‘\’, ‘‘’, ‘"’, ‘$’, and the first character of $histchars (default ‘!’).

Doing this the "zsh-way", with arrays instead of word-splitting a string, produces the desired outcome:

opts=(--hidden --no-ignore --exclude ".git")
fdfind $opts

As such, my question is not how to fix it, but rather why the first example above does not work as expected.

I suspect it is because the nested quotes are not removed after the parameter expansion, but that seems odd since the docs specifically states that quotes are removed after such expansion is done.


Solution

  • ${=spec} ${==spec}

    Perform word splitting using the rules for SH_WORD_SPLIT during the evaluation of spec, [..]

    So word splitting is performed on the result of ${spec}. Word splitting does not remove quotes, the same way in POSIX sh, quotes would not be removed.

    14 Expansion

    These expansions are performed on what you type. When you type echo "*" then after all expansions " are removed. These are different set of expansion from what happens with the result of ${spec}.

    6.9 Quoting

    Yes, according to quotes section, when you type echo "*" then * part is quoted. It has nothing to do with what happens with the result of expansion of ${spec}. There is also no expansion that would remove quotes.