简体   繁体   中英

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.

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.

man bash "quoting" section (my emphasis):

Enclosing characters in double quotes preserves the literal value of all characters within the quotes, with the exception of $, `, \\, and, when history expansion is enabled, !. When the shell is in posix mode, the ! has no special meaning within double quotes, even when history expansion is enabled. The characters $ and ` retain their special meaning within double quotes. The backslash retains its special meaning only when followed by one of the following characters: $, `, ", \\, or <newline>.

So backslash does not escape every character, unlike some other languages.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM