我在 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+++a
是i++ + 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 应读取足够的输入以确定要扩展的单元的末尾。