bash 变量替换和引用

bash variable substitution and quoting

提问人:mishar 提问时间:2/10/2020 更新时间:2/10/2020 访问量:812

问:

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

fred="\*"
echo $fred

结果为输出:

\*

为什么?我认为这会被替换为$fred,因此输出将是:\*

*

但事实并非如此。

bash 变量回 引用

评论


答:

2赞 l0b0 2/10/2020 #1

man bash“引用”部分(我的重点):

将字符括在双引号中可保留引号内所有字符的文本值,但 $、'、\ 和 (启用历史记录扩展时) !。当 shell 处于 posix 模式时,!在双引号中没有特殊含义,即使启用了历史记录扩展也是如此。字符 $ 和 ' 在双引号内保留了其特殊含义。只有当后面跟着以下字符之一时,反斜杠才保留其特殊含义:$、'、“、\ 或 <换行符>。

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

评论

0赞 mishar 2/10/2020
所以根据我的理解,只有当反斜杠在双引号内时,这才是这样。这难道不意味着双引号也被替换为变量的值吗?
0赞 mishar 2/10/2020
谢谢。因此,在变量的值被子插入/引用后,它是否可以不参与 shell 解释的任何进一步的特殊含义?换言之,外壳对其特殊含义的解释是在分配之后完成的吗?(例如,在我上面的问题中,我理解反斜杠在变量赋值中被转义了。正因为如此,当引用该变量时,它现在是否总是被转义,仅仅因为它在赋值中被转义了?
0赞 mishar 2/10/2020
实际上,另一个想法是:如果我有一个变量带有星号 *,那么当我回显它的值时,无论我在哪个目录中,它都会回显我的工作目录的内容。因此,如果通配只发生在分配中,那么每次切换目录时,该变量分配是否会在后台重复发生?(对不起,如果这似乎无关)。
0赞 mishar 2/10/2020
@I0bo 针对您的评论,通配是否真的发生在分配中?我问是因为如果我只为变量 fred 分配一个星号 *,然后回显“$fred”,那么我就会期望无论我在哪个目录中,我都会从我分配它时所在的目录中获得文件名列表。然而,相反,只打印了一个星号。
0赞 l0b0 2/10/2020
不,这是不正确的。我已经引用了很多年了,所以我不再知道未引用变量的规则:)
3赞 KamilCuk 2/10/2020 #2

来自 bash 手册 Shell 扩展强调我的:

扩展的顺序为:大括号扩展;波浪号扩展、参数和变量扩展、算术扩展和命令替换(以从左到右的方式完成);拆词;和文件名扩展。

在可以支持它的系统上,还有一个额外的扩展可用:流程替代。这与波浪号、参数、变量、算术扩展和命令替换同时执行。

执行这些扩展后,将删除原始单词中存在的引号字符,除非它们本身已被引用(引号删除)。

那么引号删除扩展是可以替代的东西,强调我的:\**

在前面的扩展之后,将删除所有不是由上述扩展之一产生的字符 '\'、''' 和 ''' 的未加引号的出现。

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

变量值周围的引号是否也会被替换为该变量?

不。 包含两个字符。$fred\*

为什么?

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


您可能对文件名扩展感兴趣。当单词拆分扩展后的“单词”对于文件名扩展“合格”时,就会发生文件名扩展。“word”限定用于文件名扩展,例如,如果存在不在单引号或双引号内且未转义的字符。in 是用反斜杠字符转义的,所以这里不是“图案字符”......在结果中,不执行文件名扩展。**\**

通配是否真的发生在分配时?

不。“Globing”即。变量赋值时不执行文件名扩展。从 bash shell 参数

变量可以通过以下形式的语句赋值

name=[value]

...不执行文件名扩展。...

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

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

所以 assings to (two 代替 single )。然后在 中,因为反斜杠是转义的斜杠,所以左边没有转义,所以它是一个模式字符,并且该单词符合文件名扩展的条件。fred="\\\*"\\*fred\\\echo $fred\\*