bash 如何解释三重括号?

bash 如何解释三重括号?

我在 bash 中看到命令

echo $(((i=18)))

打印 18。这让我明白 $(((i=18))) 被解释为算术扩展(变量 i 在构造内部初始化)。不过,人们也可以考虑命令替换

$(command)

((i=18))

command.事实上,看起来命令替换来了算术展开式(学习 bash Shell,奥莱利 2005 年,第 17 页。 181)。因此,结果并不是人们所期望的。你如何解释这一点?

答案1

沃特。$(((i=18))),你是对的,它可以是$(( (i=18) )), 或$( ((i=18)) ),两者都是有效的。有点常见(而且绝对是简单的)解释这样不明确情况的方法是识别最长的有效运算符。这意味着这将被解释为$((后面跟着(

这就是 Bash 和 Ksh 中的 eg 发生的情况<<(:它<<后面跟着(,而不是<后面跟着<(尽管第一个解释是语法错误,而第二个解释是有效的!用户必须添加一个空格来帮助 shell 找到它。同样,i+++ai++ + a和不是i + ++a。其他语言中也存在类似的情况。

但对于 来说$(((,这并不完全是事实。有些 shell 看起来确实更远,有些则不然。考虑例如这个:

echo $((echo hi); (echo ho))

如果解释为命令替换,则它是有效的,并打印hi ho.但贪婪地认识$((会将其解释为算术展开式,因此,它完全是假的。

除了 Dash 和 Busybox 之外,我尝试过的所有 shell 都能识别那里的有效命令替换。在前两个括号之间放置一个空格可以使其明确。

事实上,看起来命令替换发生在算术扩展之前

不,它们发生在同一处理点。要看到这一点,请创建一个命令替换,将其扩展为有效的算术扩展。例如echo '$((1+2))'印刷品$((1+2));所以$( echo '$((1+2))' )扩展到$((1+2)).但这不会在同一命令中进一步处理。

这,

echo $( echo '$((1+2))' )

输出$((1+2))而不是3.

当然,算术展开只能产生数字,因此无法测试相反的顺序。但是,可以在变量/参数扩展和命令替换之间进行类似的实验,并且在任何情况下,一次扩展的结果都不会进一步扩展。


支撑扩张,另一方面又不同。

Bash 在变量扩展之前处理它:

$ bash -c 'v=000 va=123 vb=456; echo $v{a,b}; n=1 m=4; echo {$n..$m}'
123 456
{1..4}

而 Ksh 则相反:

$ ksh -c 'v=000 va=123 vb=456; echo $v{a,b}; n=1 m=4; echo {$n..$m}'
000a 000b
1 2 3 4

(在 Ksh 中,甚至a="{1.."; b="4}"; echo $a$b展开大括号,并输出1 2 3 4。Zsh 在这里又是理智的,它首先展开变量,但不会让展开的大括号触发进一步的展开。Zsh 也识别<<(< <(。)

当然,还有分词和文件名生成,其中发生毕竟,但只是为了结果未引用的扩展。

答案2

此行为与中所述的一致Bash 的 CHANGES 文件:

$((...))总是首先解析为算术扩展,而不是像 Posix 所要求的那样解析为潜在的嵌套命令替换。

的确,POSIX 承认存在歧义,必须以算术展开式为准:

shell 命令语言的语法对于以“ $((”开头的扩展存在歧义,这可能会引入算术扩展或以子 shell 开头的命令替换。算术展开优先;也就是说,shell 应首先确定它是否可以将扩展解析为算术扩展,并且如果确定无法将扩展解析为算术扩展,则仅应将扩展解析为命令替换。

现在你的句子中的一些词“看起来命令替换出现在算术扩展之前”:不,POSIX 指定单词扩展从头到尾发生,即从左到右,按照它们遇到的顺序:

应从头到尾执行波形符扩展、参数扩展、命令替换和算术扩展。

“令牌识别”部分给出了一些进一步的精度:

如果当前字符是不带引号的 '$' 或 '``',shell 应从其介绍性不带引号的字符序列中识别参数扩展、命令替换或算术扩展的任何候选者的开头:'$' 或 "${",分别为“$(”或“`”和“$((”。shell 应读取足够的输入以确定要扩展的单元的末尾。

相关内容