shell 逻辑运算符 &&、|| 的优先级

Mar*_*ter 173 shell bash

我试图了解逻辑运算符优先级在 bash 中是如何工作的。例如,我会期望以下命令不会回显任何内容。

true || echo aaa && echo bbb
Run Code Online (Sandbox Code Playgroud)

然而,出乎我的意料,bbb被打印出来了。

有人可以解释一下,我如何理解bash 中的复合&&||运算符?

Jos*_* R. 168

在许多计算机语言中,具有相同优先级的运算符是左结合的。也就是说,在没有分组结构的情况下,最左边的操作首先执行。Bash也不例外

这是重要的,因为,在击,&&并且||具有相同的优先级。

所以在你的例子中发生的是最左边的操作 ( ||) 首先执行:

true || echo aaa
Run Code Online (Sandbox Code Playgroud)

由于true显然是正确的,因此||运算符短路并且整个语句都被认为是正确的,而无需echo aaa像您期望的那样进行评估。现在要做最右边的操作:

(...) && echo bbb
Run Code Online (Sandbox Code Playgroud)

由于第一个操作评估为真(即退出状态为 0),就好像您正在执行

true && echo bbb
Run Code Online (Sandbox Code Playgroud)

所以&&不会短路,这就是为什么你看到bbb回声。

你会得到相同的行为

false && echo aaa || echo bbb
Run Code Online (Sandbox Code Playgroud)

基于评论的注释

  • 您应该注意,只有当两个运算符具有相同的优先级时,遵循左结合规则。当您将这些运算符与诸如[[...]]or 之类的关键字结合使用时,情况就不是这样了,((...))或者使用-oand-a运算符作为testor[命令的参数。在这种情况下,AND(&&-a)优先于 OR(||-o)。感谢 Stephane Chazelas 的评论澄清了这一点。
  • 似乎在 C和类 C 语言&&中的优先级高于||这可能是您期望原始构造表现得像的原因

    true || (echo aaa && echo bbb). 
    
    Run Code Online (Sandbox Code Playgroud)

    然而,这不是 Bash 的情况,其中两个运算符具有相同的优先级,这就是 Bash 使用左结合规则解析表达式的原因。感谢凯文提出这个问题的评论。

  • 也可能存在评估所有 3 个表达式的情况。如果第一个命令返回非零退出状态,||则不会短路并继续执行第二个命令。如果第二个命令以零退出状态返回,则&&不会短路并且将执行第三个命令。感谢 Ignacio Vazquez-Abrams 提出的评论。

  • 增加一点混乱的小提示:而 `&&` 和 `||` shell 运算符如 `cmd1 && cmd2 || cmd3` 具有相同的优先级,`((...))` 和 `[[...]]` 中的 `&&` 优先于 `||` (`((a || b && c))`是`((a || (b && c))))`)。`test`/`[` 中的 `-a`/`-o` 和 `expr` 中的 `find` 和 `&`/`|` 也是如此。 (30认同)
  • 在类 c 语言中,`&&` 比 `||` 具有更高的优先级,因此会发生 OP 的预期行为。习惯了这些语言的粗心用户可能没有意识到它们在 bash 中具有相同的优先级,因此可能值得更明确地指出它们在 bash 中确实具有相同的优先级。 (20认同)
  • 今天,对我来说,“bash 运算符优先级”在 Google 中排名第一的是 http://tldp.org/LDP/abs/html/opprecedence.html...它声称 && 的优先级高于 || 。然而@JosephR。显然在事实上和法律上也是正确的。Dash 的行为与在 http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html 中搜索“优先级”的行为相同,我认为这是 POSIX 要求,因此我们可以依赖它。我发现的所有错误报告都是作者的电子邮件地址,在过去的六年中,该地址肯定已被垃圾邮件发送至死。反正我会试试... (3认同)
  • 另请注意,在某些情况下,*所有三个*命令都可以运行,即中间命令可以返回 true *或* false。 (2认同)
  • @MartinDorey 我以与您相同的方式阅读该页面,但是在阅读了上述答案后,我现在将空白行解释为“组分隔符”,以便相邻(非空白)行具有相同的优先级。 (2认同)

Oli*_*Oli 86

如果您希望多项事情取决于您的情况,请将它们分组:

true || { echo aaa && echo bbb; }
Run Code Online (Sandbox Code Playgroud)

那什么都不打印,而

true && { echo aaa && echo bbb; }
Run Code Online (Sandbox Code Playgroud)

打印两个字符串。


发生这种情况的原因比约瑟夫想象的要简单得多。记住 Bash 用||和做什么&&。这都是关于上一个命令的返回状态。查看原始命令的字面意思是:

( true || echo aaa ) && echo bbb
Run Code Online (Sandbox Code Playgroud)

第一个命令 ( true || echo aaa) 以 退出0

$ true || echo aaa; echo $?
0
$ true && echo aaa; echo $?
aaa
0

$ false && echo aaa; echo $?
1
$ false || echo aaa; echo $?
aaa
0
Run Code Online (Sandbox Code Playgroud)

  • 任何被 `(...)` 和 `{...}` 符号混淆的人都值得一读:[命令分组手册](http://www.gnu.org/software/bash/manual/html_node/Command-Grouping .html) (15认同)
  • 注意最后的半列`;`。没有这个,它将无法工作! (12认同)
  • +1 用于实现 OP 的预期结果。请注意,您放在 `(true || echo aaa) && echo bbb` 中的括号正是我正在制作的。 (7认同)
  • 我遇到了这个问题,因为我在最后一个命令之后遗漏了尾随分号(例如,第一个示例中的“echo bbb;”)。一旦我发现我错过了那个,这个答案正是我正在寻找的。+1 帮助我弄清楚如何完成我想要的! (2认同)

Doc*_*ger 22

&&||运营商都没有确切的内嵌替代IF-THEN-ELSE。尽管如果小心使用,它们可以完成很多相同的事情。

一个单一的测试是直接和明确的......

[[ A == A ]]  && echo TRUE                          # TRUE
[[ A == B ]]  && echo TRUE                          # 
[[ A == A ]]  || echo FALSE                         # 
[[ A == B ]]  || echo FALSE                         # FALSE
Run Code Online (Sandbox Code Playgroud)

但是,尝试添加多个测试可能会产生意想不到的结果......

[[ A == A ]]  && echo TRUE   || echo FALSE          # TRUE  (as expected)
[[ A == B ]]  && echo TRUE   || echo FALSE          # FALSE (as expected)
[[ A == A ]]  || echo FALSE  && echo TRUE           # TRUE  (as expected)
[[ A == B ]]  || echo FALSE  && echo TRUE           # FALSE TRUE   (huh?)
Run Code Online (Sandbox Code Playgroud)

为什么 FALSETRUE 都会回显?

这里发生的事情是我们没有意识到&&并且||是重载的运算符,它们在条件测试括号内的行为与[[ ]]我们这里的 AND 和 OR(条件执行)列表中的行为不同。

从 bash 联机帮助页(已编辑)...

列表

列表是由运算符 ;、&、&& 或 ?? 之一分隔的一个或多个管道的序列,并且可选地以 ;、& 或 之一终止。在这些列表运算符中,&& 和 ?? 具有相同的优先级,其次是 ; 和 & 具有相同的优先级。

一个或多个换行符的序列可能会出现在列表中,而不是用分号来分隔命令。

如果命令由控制运算符 & 终止,shell 将在子 shell 的后台执行该命令。shell 不等待命令完成,返回状态为 0。命令以 ; 分隔。依次执行;shell 依次等待每个命令终止。返回状态是最后执行的命令的退出状态。

AND 和 OR 列表是由 && 和 ?? 分隔的多个管道之一的序列。分别控制运算符。AND 和 OR 列表以左结合性执行。

AND 列表的形式为 ...
command1 && command2
当且仅当 command1 返回退出状态为零时,才会执行 Command2。

OR 列表的形式为 ...
command1 ?? command2
当且仅当 command1 返回非零退出状态时,才会执行 Command2。

AND 和 OR 列表的返回状态是列表中执行的最后一个命令的退出状态。

回到我们的最后一个例子......

[[ A == B ]]  || echo FALSE  && echo TRUE

[[ A == B ]]  is false

     ||       Does NOT mean OR! It means...
              'execute next command if last command return code(rc) was false'

 echo FALSE   The 'echo' command rc is always true
              (i.e. it successfully echoed the word "FALSE")

     &&       Execute next command if last command rc was true

 echo TRUE    Since the 'echo FALSE' rc was true, then echo "TRUE"
Run Code Online (Sandbox Code Playgroud)

好的。如果这是正确的,那么为什么最后一个示例会回显任何内容?

[[ A == A ]]  || echo FALSE  && echo TRUE


[[ A == A ]]  is true

     ||       execute next command if last command rc was false.

 echo FALSE   Since last rc was true, shouldn't it have stopped before this?
                Nope. Instead, it skips the 'echo FALSE', does not even try to
                execute it, and continues looking for a `&&` clause.

     &&       ... which it finds here

 echo TRUE    ... so, since `[[ A == A ]]` is true, then it echos "TRUE"
Run Code Online (Sandbox Code Playgroud)

使用多个命令&&||在命令列表中使用时出现逻辑错误的风险非常高。

建议

单个&&||在命令列表中按预期工作,因此非常安全。如果是不需要 else 子句的情况,则可以更清楚地遵循以下内容(需要花括号将最后 2 个命令分组)...

[[ $1 == --help ]]  && { echo "$HELP"; exit; }
Run Code Online (Sandbox Code Playgroud)

Multiple &&and||操作符,除了最后一个操作符之外的每个命令都是一个测试(即在括号内[[ ]]),通常也是安全的,但最后一个操作符的行为与预期一致。最后一个运算符的作用更像是一个thenorelse子句。