我在 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
中):ksh
zsh
$ 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