【问题标题】:Is there a difference between negating before/after a test command?在测试命令之前/之后否定有区别吗?
【发布时间】:2016-07-14 07:01:12
【问题描述】:

我一直在编写 Bash 脚本,我想知道这两种形式的否定与 test 命令之间是否有任何区别:

if [ ! condition ]; then
fi

if ! [ condition ]; then
fi

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

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

(我似乎记得不久前读过一篇讨论这个问题的文章,但我不记得如何找到它/到底讨论了什么。)

【问题讨论】:

  • 并不是一个真正的陷阱,因为很难构建一个会发生这种情况的现实场景,但请记住,在 ! [ condition ] 中,! 是一个 shell 运算符,否定了退出状态命令,而在[ ! condition ] 中,! 只是[ 命令的字符串参数。在foo="! bar" 之后,比较[ "$foo" ][ $foo ]

标签: linux bash shell unix terminal


【解决方案1】:

如果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'

echos 没有输出。

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

【讨论】:

    【解决方案2】:

    chepner对该问题的深刻评论为基础:

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

    • ! [ condition ] 中,!一个 shell 关键字,它否定它后面的任何命令(恰好是 [ 在本例)。

    ! [ condition ] 语法隐含地为您提供的一件事是,否定适用于任何 [ condition ] 评估为作为一个整体,所以如果这是意图,您不必担心运算符优先级。

    性能方面,您选择哪种语法可能没有太大区别;快速测试建议:

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

    • 如果! 用作关键字,因此您可以通过不担心优先级来简化条件,它可能会稍微快一些(例如,! [ 0 -o 1 ] vs .[ ! \( 0 -o 1 \) ];请注意,POSIX 规范不鼓励使用-a-o,因为它存在歧义)。

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

    • 提供更多功能
    • 允许您安全地将表达式与&&|| 组合在一起
    • 惊喜更少
    • 也稍微快一些(尽管这在实践中并不重要)

    见我的this answer

    【讨论】:

      【解决方案3】:

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

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

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

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

      对于给定的表达式:

      • 一方面,您否定以真实表达式状态退出的test 命令。
      • 另一方面,你否定一个表达式,test 命令(或[)以错误状态退出

      两者的效果相同。

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

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

      【讨论】:

      • 您可能会注意到,第一组中第三个示例的小修改也报告了1:test ! false || echo $?。这是因为测试正在检查字符串(truefalse)是否为空。与第一组中的第二个示例类似;用false 替换true 不会改变结果。
      • 你是对的。将其替换为我应该是的:一个表达式。
      • 也许您应该注意==bash 有效,但对dash 无效。
      • 使用 POSIX 语法更新。
      猜你喜欢
      • 2017-03-28
      • 1970-01-01
      • 2014-11-12
      • 2021-12-25
      • 2017-11-08
      • 1970-01-01
      • 1970-01-01
      • 2019-07-19
      • 2013-03-03
      相关资源
      最近更新 更多