在测试命令之前/之后否定之间有区别吗?

时间:2016-03-26 17:12:59

标签: linux bash shell unix terminal

我已经暂时使用Bash脚本了,我想知道这两种形式的否定与test命令之间是否存在差异:

if [ ! condition ]; then
fi

if ! [ condition ]; then
fi

第一个告诉shell传递参数! condition进行测试,让程序自己处理否定。另一方面,第二个传递condition来测试并让shell本身否定错误代码。

在选择这两种形式时,我应该注意哪些陷阱? $condition的哪些值可以使它们之间的结果不同?

(我似乎记得刚刚在讨论这篇文章的时候读过一篇文章,但我不记得如何找到它/正是所讨论的内容。)

3 个答案:

答案 0 :(得分:7)

chepner对该问题的深刻见解的基础上进行构建:

  • [ ! condition ]中,!只是 参数[内置(有效的别名) test内置);恰好[ / test将参数!解释为否定。

  • ! [ condition ]中,!一个 shell关键字,否定了所遵循的任何命令(恰好是在这种情况下[。)

! [ condition ]语法隐含地给你的一点是,否定适用于{em}作为整体评估的[ condition ],所以如果这是意图,你需要&# 39;担心运营商优先权。

以性能为导向,您选择哪种语法可能并没有太大的区别;快速测试表明:

  • 如果condition在两种情况下都是相同的,那么将!作为参数传递给[ 的速度可以忽略不计。

  • 如果将!用作关键字,并且您因此可以通过不担心优先级来简化条件,则可能会稍快一些(例如,{{ 1}}与! [ 0 -o 1 ];请注意,由于歧义,POSIX规范不建议使用[ ! \( 0 -o 1 \) ]-a

也就是说,如果我们正在谈论 Bash ,那么您应该考虑使用-o代替[[ ,因为[是一个 shell关键字,它在一个特殊的上下文中解析所附的表达式:

  • 提供更多功能
  • 允许您将表达式与[[&&
  • 安全地结合使用
  • 带来更少的惊喜
  • 也稍微快一点(虽然这在实践中很少有意义)

请参阅我的this answer

答案 1 :(得分:2)

!否定以下命令的退出代码:

$ ! test 1 = 1 || echo $?  # negate command with true expression
1

正如在手册页中所述,test(以及类似的[内置)退出,状态由以下表达式确定:

$ test ! 1 = 1 || echo $?  # return false expression
1
$ [ ! 1 = 1 ] || echo $?
1

对于给定的表达式:

  • 一方面否定了以{true}表达状态退出的test命令。
  • 另一方面,你否定了一个表达式,test命令(或[)以其错误状态退出

两者都会产生同样的效果。

所以我想说这些语法是等价的。外部!允许否定复合测试的优势:

$ ! [ 1 = 1 -a 2 = 2 ] || echo $?
1

答案 2 :(得分:0)

如果test / [遇到错误,则有所不同。考虑:

x=abc
if   [ ! "$x" -gt 0 ]; then echo "first true"; fi
if ! [   "$x" -gt 0 ]; then echo "second true"; fi

输出为:

bash: [: abc: integer expression expected
second true

[[ .. ]]不同,test / [的工作方式类似于常规实用程序,并通过返回2来指示错误。这是一个虚假的值,并且!放在方括号之外,shell会将其反转,就像测试中的常规负结果反转一样。

[[ .. ]]的行为不同,因为条件中的语法错误是shell的语法错误,并且shell本身退出并显示错误:

if [[ a b ]]; then echo true; else echo false; fi

仅打印

bash: conditional binary operator expected
bash: syntax error near `b'

echo没有任何输出。

另一方面,算术测试在[[ .. ]]中的工作方式有所不同,因此[[ "$x" -gt 0 ]]永远不会出错。