假设 bash 算术表达式中 null 或未设置的变量将被零替换是否安全?

假设 bash 算术表达式中 null 或未设置的变量将被零替换是否安全?

我在 bash 手册页(版本 4.2)的算术求值部分找到了以下语句。

当通过名称引用而不使用参数扩展语法时,为 null 或未设置的 shell 变量的计算结果为 0。

在《高级 Bash 脚本指南》中,有页面并发表以下评论。

#  Bash (usually) sets the "integer value" of null to zero
#+ when performing an arithmetic operation.
#  But, don't try this at home, folks!
#  It's undocumented and probably non-portable behavior.

以下是此行为的示例:

$ foo=
$ let 'foo += 5'
$ echo $foo
5
$ unset foo
$ let 'foo += 2'
$ echo $foo
2

假设 bash 会将 null 变量替换为 0 是否安全?假设这样会导致代码在 bash 版本之间不可移植吗?

答案1

是的,bash算术展开式是从 借用的,并且自 80 年代以来ksh一直是有记录的方式。ksh

但请注意,在 POSIX 算术扩展中$((x * 2)),行为是未指定的(由 POSIX),除非$x包含十进制数字常量(如 、2-2都可以,但空字符串 或1+1不是),这意味着您不能这样做POSIX 脚本中的假设sh

还要注意(至少在、bash中):kshzsh

$ echo $((2*$foo-2)) # actually $((2*-2))
-4
$ echo $((2*foo-2))  # actually $((2*0-2))
-2

答案2

来自手动的:

当通过名称引用而不使用参数扩展语法时,为 null 或未设置的 shell 变量的计算结果为 0。

我不知道这一点已经改变,pdksh、ksh93、dash 和 zsh 也是如此,但 POSIX 没有指定行为。

请注意, if set -o nounset(aka set -u) 有效,$((a))或者如果未设置则((a+=1))导致错误(在 bash 和 ksh93 中,但在 dash、pdksh 或 zsh 中则不然)。a

另请注意,这只适用于直接在算术表达式中使用参数时$((a+1)),而不适用于使用参数替换时$(($a+1))。对比:

$ unset a
$ echo $((1 - a + 2))   # 1 - 0 + 2
3
$ echo $((a - $a + 2))  # 1 - (+2)
-1

答案3

可以安全地假设 bash 在任何相当新的系统上都会用 0 替换 null 变量。

正如 bash 4.2 的联机帮助页中所记录的那样,它肯定至少是该版本的记录行为。正如高级 Bash 脚本指南中所描述的那样(但未记录),您可以预期在编写该指南时可用的任何 bash 版本中都有这种行为。它可能可以追溯到 bash 的早期版本。

但是,如果您想绝对严格,则需要检查您想要支持的所有 bash 版本的文档,并且您不能对任何未明确记录行为的版本做出任何假设。

我认为你不需要绝对严格,但如果你不想冒险,你可以foo=${foo:-0}在使用它们之前将未定义的变量显式设置为 0。例子:

foo=1

foo=${foo:-0}
bar=${bar:-0}

echo "before math: foo=$foo, bar=$bar"
let 'foo += 2'
let 'bar += 2'
echo "after math: foo=$foo, bar=$bar"

输出:

before math: foo=1, bar=0
after math: foo=3, bar=2

相关内容