当使用 test 或 [ 来计算带有逻辑 NOT ! 的条件表达式时,感叹号应该放在括号内还是括号外?

当使用 test 或 [ 来计算带有逻辑 NOT ! 的条件表达式时,感叹号应该放在括号内还是括号外?

当使用 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整数验证。

相关内容