bash 脚本中逻辑 AND (&&) 和 OR (||) 的悖论,用于检查命令是否成功执行(退出代码 0 被解释为 true)

bash 脚本中逻辑 AND (&&) 和 OR (||) 的悖论,用于检查命令是否成功执行(退出代码 0 被解释为 true)

所以我很理解退出代码0被认为是程序运行成功。然后我们在 bash 脚本中使用它,ANDOR根据第一个程序的退出状态运行下一个程序。一个很好的例子可以在这里找到:https://unix.stackexchange.com/a/187148/454362

这是否意味着0被解释为真,而除零之外的任何其他数字都被解释为假?这与我所知道的所有编程语言相反。那么我可以假设 bash 内部使用逻辑NOT将退出代码反转为正确的 false / true 值吗?

所以,这些是一些例子:

#should result in false, and bash shows 0 as false
echo $(( 1 && 0 ))

# should result in true,  and bash shows 1 as true
echo $(( 1 || 0 )) 

# does not show 'hi'; even though if false is zero per first example, then
# this should be considered success exit code and 'hi' to be shown!!
false && echo 'hi' 

# does show 'hi'. It first runs the "echo 'hi'" and interprets its exit
# status of zero as success, but then false stops the 2nd hi.
echo 'hi' && false && echo ' 2nd hi' 

# shows 'hi 2nd hi'
echo 'hi' && (( 1 || 0 )) && echo ' 2nd hi'

看来我正在回答我自己的问题。但我只是想澄清是否有人知道 bash 处理的内部。

答案1

您链接到的帖子中的示例如下:

false && echo "OK"
true || echo "OK"

在这种情况下,对于进程的退出状态,是的,0是真实的,其他任何东西都是虚假的。 (是的,这里truefalse程序。可能是内置于 shell 中的,但工作原理是一样的。)

来自POSIX 定义(||类似,正如Bash 的文档说):

AND 列表
控制运算符“&&”表示 AND 列表。格式应为: 第一
command1 [ && command2] ...
命令1应予执行。如果其退出状态为零,命令2应被处决,等等……

是的,这与几乎所有其他上下文和编程语言中使用的约定相反。话又说回来,在 C 语言中,函数在成功时返回零,在错误时返回非零错误代码也很常见。这样,您就可以区分不同类型的错误(*)。当然,如果您的函数需要返回一些有用的值(例如指针),那么这实际上不起作用,并且就 C 语言而言,0 是假的。无论如何,都是不同的。

我认为对实施进行任何假设都不是一个好主意。请记住,在这种情况下,零为真,例如 (true && true) 为 (true)。

(*就像家庭一样,快乐的系统调用都是相似的。不快乐的系统调用各有各的不快乐。)


然后你的例子:

#should result in false, and bash shows 0 as false
echo $(( 1 && 0 ))

在这里,您在算术上下文中使用&&,它不遵守与退出状态相同的规则。这里,0为假,1为真。 (true AND false)也是如此1 && 0,即 (false) 或 0。

# should result in true,  and bash shows 1 as true
echo $(( 1 || 0 )) 

与上面类似。

# does not show 'hi'; even though if false is zero per first example, then
# this should be considered success exit code and 'hi' to be shown!!
false && echo 'hi'

实用程序称为false以状态 1(或至少某个非零值)退出。在这种情况下,这是错误的,因此&&通过短路逻辑跳过了右侧。

# does show 'hi'. It first runs the "echo 'hi'" and interprets its exit
# status of zero as success, but then false stops the 2nd hi.
echo 'hi' && false && echo ' 2nd hi'

一样。


# shows 'hi 2nd hi'
echo 'hi' && (( 1 || 0 )) && echo ' 2nd hi'

1 || 0在这里使用了,无论哪种方式都是正确的,并且数值在算术上下文中有些消失了。让我们试试这些:

$ echo foo && (( 0 )) && echo bar
foo
$ echo foo && (( 1 )) && echo bar
foo
bar

现在,((...))是一个算术构造(如$((...))),其中 0 为假。与 不同$((...))((...))也是一个命令,因此具有退出状态。如果内部表达式的计算结果为非零(真值),则它以零(真值)退出;如果内部表达式的计算结果为零(假),则为 1(假)。好吧,这可能会令人困惑,但最终结果是,类似 C 的隐式比较在它内部与零工作,并且当将它与 shell 的条件一起使用时,您会得到相同的真值。

因此,while (( i-- )); do ...循环直到i为零。

(( foo ))不是标准的,但受 ksh/Zsh/Bash 支持。它的标准解释是foo两个嵌套子 shell 内的命令,因此可能会给出“命令 'foo' 未找到”错误。)

还值得指出的是,类似的东西(( true )) && echo maybe可能不会打印任何内容。在算术上下文中,普通单词被视为变量的名称(在许多 shell 中递归地),因此除非将变量true设置为非零值,否则(( true ))将为 false。


(来自混淆部门的想法是运行true=0; false=1; true() (exit 1); false() (exit 0);。现在打印什么 true && (( false )) || echo maybe && echo maybe not以及为什么?。)

答案2

是的,命令退出状态反转了整数和真/假概念之间的通常映射。

原因是命令可能会以多种不同的方式失败。只有一个零值,但有许多非零值(在本例中为 255,因为仅使用了退出状态的 8 位)。通过使用非零作为故障表示,您可以使用不同的非零值来编码不同类型的故障。

例如,grep当成功读取文件但未找到任何匹配项时,使用退出状态 1;当出现实际错误时,使用状态 2。

如果您的应用程序不关心故障模式之间的区别,您可以false通过使用if或 逻辑运算符将它们全部视为。如果你关心的话,你就会$?明确地进行比较。

这种为不同退出状态分配不同含义的方法并不常见,但在需要时使用它会很有用。1如果不需要区分,大多数程序只是将其用作失败代码。

true
echo $? # 0
false
echo $? # 1

答案3

该行为取决于外壳。假设您正在使用 Bash,答案来自 Bash 手册:

列表

...

AND 和 OR 列表是分别由&&||控制运算符分隔的一个或多个管道的序列。 AND 和 OR 列表以左关联性执行。 AND 列表的形式为

          command1 && command2

command2command1当且仅当返回退出状态为零(成功)时才执行。

OR 列表的形式为

          command1 || command2

command2command1当且仅当返回非零退出状态时才执行。 AND 和 OR 列表的返回状态是列表中最后执行的命令的退出状态。

相关内容