当使用 bashtest
或[
内置命令用逻辑 NOT 计算条件表达式时!
,感叹号应该放在括号内还是括号外?有关系吗?
这是一个简单的 MRE。
$ var=1
$ if [ $var -gt 0 ]; then echo "evaluated to true"; else echo "evaluated to false"; fi
evaluated to true
$ if ! [ $var -gt 0 ]; then echo "evaluated to true"; else echo "evaluated to false"; fi
evaluated to false
$ if [ ! $var -gt 0 ]; then echo "evaluated to true"; else echo "evaluated to false"; fi
答案1
! [ "$var" -gt 0 ]
[ ! "$var" -gt 0 ]
只要没有错误,与当前的 shell 和实用程序无关。
但请注意,您写的if ! [ $var -gt 0 ]
并且没有if [ ! $var -gt 0 ]
引号$var
确实很重要!
$var
如果为空或包含空格,则会中断。例如,您会得到这样的结果if ! [ -gt 0 ]
:测试[
将打印错误消息并返回错误的退出代码 2。然后它将被 反转!
,并且整个条件将为 true!
$ var=
$ if ! [ $var -gt 0 ]; then echo yes; fi
bash: [: -gt: unary operator expected
yes
在该特定上下文中,[ ! $var -gt 0 ]
可以被视为更安全,因为虚假错误返回将保持虚假,并且不采用分支。不过,这里最直接的问题是缺少引号,这很容易解决。
关于报价,请参见:什么时候需要双引号?
然而,即使除了引用问题之外, in 中的非数字值$var
也会给出错误[
并使条件为假。因此,您应该事先测试输入是否有效,或者以其他方式处理这种可能性。在某些情况下,出现错误时的正确操作可能是拿分支,例如,如果它包含错误出口。遗憾的是,shell 并没有真正检测错误的好方法,因为没有例外等情况。为了区分错误和错误结果,$?
需要保存并查阅原始值。
或者可以编写测试以使错误情况与适当的结果一致。例如,这里的测试是用额外的否定而不是 编写的[ "$1" -le 0 ]
,但这有一个优点,即无效的输入将触发错误退出。这仅适用于!
外部[
。
$ cat foo.sh
#!/bin/bash
if ! [ "$1" -gt 0 ]; then
echo "invalid input '$1'"
exit 1
fi
echo "doing work on '$1'"
$ bash foo.sh
foo.sh: line 3: [: : integer expression expected
invalid input ''
变量值的变化和矩阵中的不同测试:
测试 \ $n | '0' '1' 'x' -------------------+---------------------------- ------ [“$n”-le 0] |真假假(带错误) [! “$n”-gt 0] |真实 虚假 虚假(带错误) ! [“$n”-gt 0] |真假 真实(有错误)
也许有些相关的是,在过去,[
当测试中的操作数看起来像运算符时,我们知道 的版本会感到困惑。例如,像[ ! = x ]
the 之类的东西!
可以被视为否定,并且整个表达式无法被解析。人们可能担心放入!
内部[
可能会使问题变得更加复杂,但我不知道在实践中是否会发生这种情况。无论如何,符合 POSIX 的版本[
应该是安全的,至少只要您不使用-a
和-o
。
看:在 shell 变量与字符串文字比较的两侧添加前缀的目的是什么?,尤其是 Stéphane 的精彩回答(我从中截取了这个[ ! = x ]
例子)。
答案2
是的,这确实很重要。
如果将 放在!
之外[ ... ]
,则任何错误都将被忽略:
var=nono
if ! [ "$var" -gt 0 ]; then echo YES; fi
bash: [: 0.1: integer expression expected
YES
if [ ! "$var" -gt 0 ]; then echo YES; fi
bash: [: 0.1: integer expression expected
这是因为if ! command; then ...
当 的退出状态command
非零时将匹配,并且命令在出现错误和计算为 false 的[ expr ]
情况下都将具有非零退出状态。expr
仅在 ksh 中(不是在 bash 中)两者是相似的,因为在 ksh 中,非数字变量将被视为0
该上下文中的变量,而不是错误。
答案3
仅当您关心评估 的内容时才重要!
,如果您[
考虑到实用程序中的失败(例如$var
实际上不是整数,在这种情况下[
实用程序将失败并且其退出状态将非零,您可以这样做)不是因为测试评估为错误的但因为实用程序未能执行测试)。
在 中if [ ! "$var" -gt 0 ]; then ...; fi
,!
是 的一个参数[
,因此将由实用程序进行评估[
,使其在以适当的退出状态终止之前在内部否定测试的意义。 (如果$var
不是整数,则测试将失败,并且不会采用分支。)
在 中,shell 将在将实用程序用于语句之前if ! [ "$var" -gt 0 ]; then ...; fi
取消(好吧,在 0 和非零之间切换)实用程序的退出状态。 (如果不是整数,则测试将失败,shell 将否定非零退出状态并将其解释为[
if
$var
真的,并且该分支将被采用。)
对于这个简单的测试,我可能会重写测试以便[ "$var" -le 0 ]
于阅读和理解代码,并且如果需要的话,采用$var
整数验证。