bash 变量替换和引用

bash variable substitution and quoting

也许这是一个简单的问题,但我想知道引号如何与变量一起使用。变量值周围的引号是否也被替换为该变量? 例如:

fred="\*"
echo $fred

输出结果:

\*

这是为什么?我认为 \* 会代替 $fred,这样输出就会是:

*

但事实并非如此。

man bash "quoting" 部分(我的重点):

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>.

所以反斜杠不会转义每个字符,这与其他一些语言不同。

来自 bash manual Shell Expansions 强调我的:

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).

然后 quote removal 扩展是可以用 \* 代替 * 的东西,强调我的:

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

然而这里发生了一个扩展(具有重要意义)——变量扩展。 $fred 用变量扩展替换 \*\* 字符串是另一个扩展的结果,因此不会对其执行引号删除。所以它保持为 \*.

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

没有。 $fred 包含两个字符 \*.

Why is that?

如果 \ 是由另一次扩展引起的,则不会删除它。


您可能对文件名扩展感兴趣。文件名扩展 happens when 当单词拆分扩展后的“单词”“符合”文件名扩展时。例如,如果 * 字符不在单引号或双引号内且未转义,则“单词”符合文件名扩展条件。 \* 中的 * 被反斜杠字符转义,所以 * 在这里不是“模式字符”...结果文件名扩展没有执行。

does globbing actually occur at assignment?

没有。 “全球”即。变量赋值时不执行文件名扩展。来自 bash Shell Parameters:

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

name=[value]

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

有趣的是:您可以使用 3 个斜杠触发文件名扩展:

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

因此 fred="\\*"\* 分配给 fred(两个 \ 替换为单个 \)。然后在 echo $fred 中,因为在 \ 中反斜杠转义了 baskslash,左边的 * 没有转义,所以它是一个模式字符,这个词符合文件名扩展的条件。