带引号的 Shell 算术扩展

koj*_*iro 5 shell arithmetic

在 Bash 和 Dash 中,在算术扩展中使用引号是非法的:

$ bash -c 'x=123;echo $(("$x"))'
bash: "123": syntax error: operand expected (error token is ""123"")
$ dash -c 'x=123;echo $(("$x"))'
dash: 1: arithmetic expression: expecting primary: ""123""
Run Code Online (Sandbox Code Playgroud)

Bash 在作为 sh 调用时给出相同的错误。不过,Ksh 和 FreeBSD 的 Bourne Shell 并不介意:

$ ksh -c 'x=123;echo $(("$x"))'
123
$ sh -c 'x=123;echo $(("$x"))'
123
Run Code Online (Sandbox Code Playgroud)

根据Bash 参考手册

该表达式被视为在双引号内,但括号内的双引号不会被特殊处理。所有代币……都经过……报价删除。

(这与POSIX所说的基本相同。)

最后,$(( ))与其他算术上下文(( ))(例如,在条件表达式中)相比,Bash 的处理方式有所不同。后者用引号很好。

要么我不明白这里的引用删除意味着什么,要么这是其中一些 shell 实现中的错误。如果是前者,“引用删除”实际上是什么意思?或者,这只是一个错误?

dan*_*uer 2

我很困惑这是一个糟糕的实现还是糟糕的文档。Bash 对于引用删除是这样说的:

报价删除

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

我认为关键可能是该段中的“所有未引用的事件”。根据文档,里面的所有内容都$(( ))被视为用双引号引起来。如果这些字符位于括号内,则它们都会被引用,从而使引号删除基本上成为一个无用的操作。例如,请注意如何处理其他“已删除”字符(另请注意如何保留尾随空格,因为如何解析带引号的字符串):

$ echo $(( '5' ))
bash: '5' : syntax error: operand expected (error token is "'5' ")
$ echo $(( \ ))
bash: \ : syntax error: operand expected (error token is "\ ")
Run Code Online (Sandbox Code Playgroud)

浏览源代码,引号确实需要平衡,因为代码会扫描以确定是$(( ))数学还是嵌套的遗留子表达式。当字符串被识别为算术表达式时,它会被解析为双引号 - 这意味着在引号删除发生之前,内部的所有字符都被视为已被引用。

就我个人而言,这就是我更喜欢 ksh 的部分原因 - 特别是对于数学。例如,它将上面的单引号 5 视为 C 字符串,其计算结果为 53。 man ascii看看为什么这是有道理的。:)