我正在看 Kusalananda 和 xhienne 之间的讨论这里,其中提到这不是一个有效的测试,会在其他 POSIX 兼容 shell[ "" -ge 2 ]
中产生错误。bash --posix
bash-4.3$ [ "" -gt 10 ]
bash: [: : integer expression expected
bash-4.3$ [ '' -gt 10 ]
bash: [: : integer expression expected
那里一切都好。出于好奇,我尝试了同样的操作[[
。
bash-4.3$ [[ "" -gt 10 ]] && echo "YES"
bash-4.3$ [[ "" -gt 0 ]] && echo "YES"
bash-4.3$ [[ "" -gt -1 ]] && echo "YES"
YES
bash-4.3$ [[ "" -eq 0 ]] && echo "YES"
YES
正如您所看到的,没有错误,它实际上被计算为数字表达式,其中“”等于 0。那么这里到底发生了什么?[[
仅仅是与旧的test
或 POSIX不一致吗?它只是执行字符串比较而不是数字比较吗?
答案1
[
和之间的一个区别[[
是[
之间不是进行算术评估,但[[
会:
$ [ "2 + 2" -eq 4 ] && echo yes
bash: [: 2 + 2: integer expression expected
$ [[ "2 + 2" -eq 4 ]] && echo yes
yes
第二个微妙之处是,无论在哪里算术评估在 bash 下执行,空字符串的计算结果为 0。例如:
$ x=""; echo $((0 + x))
0
$ [[ "" -eq 0 ]] && echo yes
yes
文档
从man bash
:
允许 Shell 变量作为操作数;参数扩展在计算表达式之前执行。在表达式中,shell 变量也可以通过名称引用,而不使用参数扩展语法。当通过名称引用而不使用参数扩展语法时,为 null 或未设置的 shell 变量的计算结果为 0。当引用变量时,或者当为使用声明 -i 赋予整数属性的变量赋值时,变量的值将被计算为算术表达式。 空值的计算结果为 0。 shell 变量无需打开其整数属性即可在表达式中使用。 [添加了强调]
旁白:安全问题
请注意,bash 的算术评估是一个潜在的安全问题。例如,考虑:
x='a[$(rm -i *)]'
[[ x -eq 0 ]] && echo yes
使用该-i
选项,上述操作是安全的,但一般教训是不要对未经净化的数据使用 bash 的算术评估。
相比之下,使用 时[
,不会执行算术评估,因此该命令从不尝试删除文件。相反,它会安全地生成错误:
$ x='a[$(rm -i *)]'
$ [ "$x" -eq 0 ] && echo yes
bash: [: a[$(rm -i *)]: integer expression expected
有关此问题的更多信息,请参阅这个答案。
答案2
是的,在数值比较中 posix test ( [
) 不会将字符串转换为数字:
$ sh -c '[ 2+2 -eq 4 ]'
sh: 1: [: Illegal number: 2+2
$ dash -c '[ 2+2 -eq 4 ]'
dash: 1: [: Illegal number: 2+2
$ bash -c '[ 2+2 -eq 4 ] && echo "YES"'
bash: line 0: [: 2+2: integer expression expected
然而,并非所有 shell 都以相同的方式工作:
$ ksh -c '[ 2+2 -eq 4 ] && echo "YES"'
YES
通常的解决方法
确保将 null 或空值转换为 0(适用于大多数 shell)
$ dash -c 'a=""; [ "${a:-0}" -gt 3 ] && echo "YES"'
使用算术
使用算术扩展(也可以像2+2
在某些 shell 中那样转换值(不是破折号))
$ dash -c 'a=""; [ "$((a+0))" -gt -3 ] && echo "YES"'
YES
使用 [[
使用该[[
测试将转换大多数字符串,这些字符串将在允许的 shell 中变成数字(即使不需要)[[
:
$ [[ "2+2" -gt 3 ]] && echo "YES"
YES