Search code examples
bashvariablesechoquoting

bash variable substitution and quoting


Perhaps it's a simple question, but I'm wondering how quotes work with variables. Do quotes around a variable's value also get subbed in for that variable? For instance:

fred="\*"
echo $fred

results in the output:

\*

Why is that? I would think that \* would get subbed in for $fred, so that the output would then be:

*

but that's not what occurs.


Solution

  • From bash manual Shell Expansions emphasis mine:

    The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done in a left-to-right fashion); word splitting; and filename expansion.

    On systems that can support it, there is an additional expansion available: process substitution. This is performed at the same time as tilde, parameter, variable, and arithmetic expansion and command substitution.

    After these expansions are performed, quote characters present in the original word are removed unless they have been quoted themselves (quote removal).

    Then quote removal expansion is something that could substitute \* for *, emphasis mine:

    After the preceding expansions, all unquoted occurrences of the characters ‘\’, ‘'’, and ‘"’ that did not result from one of the above expansions are removed.

    However one expansion (with significance) happens here - variable expansion. $fred is substituted for \* with variable expansion. The \* string is the result of another expansion, so quote removal is not performed on it. So it stays as \*.

    Do quotes around a variable's value also get subbed in for that variable?

    No. $fred contains two characters \*.

    Why is that?

    The \ is not removed if it resulted from another expansion.


    You may be interested in filename expansion. Filename expansions happens when when the "word" after word splitting expansion is "qualified" for filename expansion. The "word" is qualified for filename expansion for example if there is a * character that is not within single or double quotes and is not escaped. The * in \* is escaped with the backslash character, so * is not a "pattern character" here... In result filename expansion is not performed.

    does globbing actually occur at assignment?

    No. "Globing" ie. filename expansion is not performed on variable assignment. From bash Shell Parameters:

    A variable may be assigned to by a statement of the form

    name=[value]
    

    ... Filename expansion is not performed. ...

    On the fun side: you can trigger filename expansion with 3 slashes:

    fred="\\\*"
    touch '\file_with_backslash'
    echo $fred  # will output `\file_with_backslash` 
                # and other files you have with leading backslash...
    

    So fred="\\\*" assings \\* to fred (two \\ are substituted for single \). Then in echo $fred, because in \\ backslash is escaping the baskslash, the left * is not escaped, so it is a pattern character and the word qualifies for filename expansion.