为啥在shell中0为真而假为1?

Posted

技术标签:

【中文标题】为啥在shell中0为真而假为1?【英文标题】:Why 0 is true but false is 1 in the shell?为什么在shell中0为真而假为1? 【发布时间】:2011-02-25 09:43:27 【问题描述】:
false; echo $?

上面会输出1,这和我知道的所有其他编程语言都是矛盾的。

这有什么原因吗?

【问题讨论】:

这也符合 Unix Way... 成功时不返回任何内容。 因为退出状态不是布尔值。就这么简单。 请记住,false 不像其他编程语言那样是布尔值。它只是一个位于/bin/false(Mac 上为/usr/bin/false)的程序,旨在始终返回错误退出代码1。true 类似。所以这里没有铸造之类的东西。这只是关于退出代码的全部内容。 外壳是操作系统的(用户)界面。 Unix 等程序遵循以 0 为 OK 退出的约定, 原因在答案中给出(能够传达多个失败原因)。 shell 只是保持这个约定,这使得像if myprog; then echo OK; fi 这样的构造简单直观。否则,您将不得不反转每个测试以确保程序成功! 【参考方案1】:

这是一种约定,但仔细想想,它特别有用。一般来说,如果一个程序成功了,这就是你需要知道的一切。但是,如果失败,您可能需要了解有关失败的各种信息——为什么会发生、如何修复等等。零均值“成功”和非零均值失败让您可以很容易地检查是否成功,并根据需要调查特定错误以获取更多详细信息。许多 API 和框架都有类似的约定 - 成功的函数返回 0,失败的函数返回描述特定失败情况的错误代码。

【讨论】:

我理解这个答案,但我仍然不明白为什么 bool 到 int 的转换会被反转。是否有一个“返回布尔约定”表示:如果返回 true,则表示没有错误,如果返回 false,则表示发生错误(如更广为人知的“整数 = 错误代码约定”)? 假设有一个布尔到 int 的转换意味着你并没有真正理解答案。零表示成功,所有 1、2、...、255 都是错误代码,可以有效地传达不同的失败场景。一个特殊的例子是xargs,它使用127 左右范围内的不同错误代码来指示group 命令是如何失败的。可以进行的转换是 int 到 bool,其中 0 映射为成功(我猜您想将其表示为 true / 1;但要意识到这只是另一个任意约定),所有其他值都映射为失败。【参考方案2】:

Bash 是一种编程(脚本)语言,但它也是一种外壳和用户界面。如果0是错误的,那么程序只能出现一种错误。

但是在 Bash 中,任何非零值都是错误,我们可以使用 1-255 之间的任何数字来表示错误。这意味着我们可以有许多不同类型的错误。 1 是一般错误,126 表示无法执行文件,127 表示“找不到命令”等。这里是 Bash Exit Codes With Special Meanings 的列表,其中显示了一些最常见的退出代码。

成功也有很多种(退出状态为0)。但是,成功将允许您继续下一步 - 您可以将结果打印到屏幕上,或执行命令等。

【讨论】:

这个实用的答案显示了各种返回代码的有用性,比其他一些答案的说教更可取。 为了好玩,我要指出/usr/include/sysexits.h notes exit values 可能更具抱负,尽管它们所代表的惯例可以追溯到 1980 年代。此约定存在于 bash 范围之外。 一个伟大、简单和实用的解释。这需要在顶部。 “如果0是错误的,那么程序只能出现一种错误”。这句话很关键,也是这个答案应该放在首位的原因。 @LuisLavaire 这是未定义的行为。在实践中,这个数字往往会被截断。但如果运行时产生警告和/或简单地崩溃,那绝对不会“更错误”。操作系统为此信息保留了一个字节,并且尝试放置多个字节肯定会出错。但操作系统设计人员可能在第一天就遇到了崩溃,他们耸了耸肩,并决定他们能做的最好的就是截断值并继续前进。【参考方案3】:

这里有两个相关的问题。

首先,OP 的问题,为什么在 shell 中 0 为真而假为 1? 第二个,为什么应用程序返回 0 表示成功,而返回非零表示失败?

要回答 OP 的问题,我们需要了解第二个问题。这篇文章的众多答案都描述了这是一个约定,并列出了该约定提供的一些细节。下面总结了其中一些细节。

为什么应用程序返回 0 表示成功,而返回非 0 表示失败?

调用操作的代码需要知道关于操作退出状态的两件事。 操作是否成功退出? [*1] 如果操作没有成功退出为什么操作退出不成功? 任何值都可以用来表示成功。但是 0 比任何其他数字都更方便,因为它可以在平台之间移植。总结 xibo 在 2011 年 8 月 16 日对此question 的回答:

零与编码无关。

如果我们想将 one(1) 存储在 32 位整数字中,第一个问题将是“big-endian word 还是 little-endian word?”,然后是“组成 little-endian 的字节有多长- endian word?”,而零看起来总是一样的。

还需要预料到,有些人会在某些时候将 errno 转换为 char 或 short,甚至浮动。 (int)((char)ENOLCK) 当 char 不是至少 8 位长(UNIX 支持 7 位 ASCII 字符机器)时不是 ENOLCK,而 (int)((char)0) 为 0,与char的建筑细节。

一旦确定 0 将是成功的返回值,那么使用任何非零值来表示失败是有意义的。这允许许多exit codes 回答操作失败的问题。

为什么shell中0为真而假为1?

shell 的基本用途之一是通过编写脚本来自动化流程。通常这意味着调用一个操作,然后根据操作的退出状态有条件地执行其他操作。 Philippe A. 在他对这篇文章的回答中很好地解释了

在 bash 和一般的 unix shell 中,返回值不是布尔值。 它们是整数退出代码。

然后有必要将这些操作的退出状态解释为布尔值。将成功 (0) 退出状态映射为 true 并将任何非零/失败退出状态映射为 false 是有意义的。这样做允许有条件地执行链式 shell 命令。

这是一个示例mkdir deleteme && cd $_ && pwd。因为 shell 将 0 解释为 true,所以这个命令很方便地按预期工作。如果 shell 将 0 解释为 false,那么您必须反转每个操作的解释退出状态。

简而言之,考虑到应用程序成功退出状态返回 0 的约定,shell 将 0 解释为 false 是没有意义的。


[*1]:是的,很多时候操作需要返回的不仅仅是简单的成功消息,但这超出了本线程的范围。

另请参阅高级 Bash 脚本指南中的 Appendix E

【讨论】:

您好,只是强调一下,如果 shell 将零解释为假,非零解释为真,那么 mkdir deleteme && cd _$ && pwd 的技巧不会真的失败;但我们必须用mkdir deleteme || cd _$ || pwd 替换它,在我看来这还不太清楚,因为我们真正想做的是mkdir deletemeandcd _$and pwd...(这里的“and”具有普通语言的含义)。 我想我在回答中已经涵盖了这一点。不过要清楚,当您反转逻辑时,仅将 && 运算符替换为 || 运算符是不够的。您需要完全应用德摩根定律。见:en.wikipedia.org/wiki/De_Morgan%27s_laws 另一个考虑:我看到至少三个原因,为什么在一般情况下,很自然地说true 对应于零而false 对应于非零。首先,如果我告诉你一些事情,“告诉你真相”取决于我说谎的数量:要么是零,我已经(全球)说了实话,要么是非零,我撒了谎。这与希望逻辑 and 对应于加法的常见“与”基本相同(显然,当限制为自然数时)。 (接上一条评论)。第二个原因是有一个真理,但有许多非真理。因此,将true 的唯一值和false 的所有其他值关联起来更方便。 (这与程序返回值的考虑基本相同)。 (接上一条评论)。第三个原因是“真那个真那个A”和“真那个A”一样,“假那个假那个A”和“假那个A”一样,等等;因此true 表现为乘法 1,false 表现为乘法 -1。其中,作为 -1 的幂,意味着 true 的行为附加地为“偶数”,false 的行为为“奇数”。同样,true 应该为零......【参考方案4】:

我认为需要理解的一个基本点是这一点。通常,在 bash 和 unix shell 中,返回值不是布尔值。它们是整数退出代码。因此,您必须按照惯例对它们进行评估,即 0 表示成功,其他值表示错误。

使用 test[ ][[ ]] 运算符,如果退出代码为 0(/bin/true 的结果),bash 条件评估为 true。否则他们评估为假。

字符串的评估方式与退出代码不同:

if [ 0 ] ; then echo not null ; fi
if [ $(echo 0) ] ; then echo not null ; fi

if [ -z "" ] ; then echo null ; fi

(( )) 算术运算符将 1 和 0 解释为真假。但该运算符不能完全替代test[ ][[ ]]。下面是一个显示算术运算符何时有用的示例:

for (( counter = 0 ; counter < 10 ; counter ++ )) ; do
  if (( counter % 2 )) ; then echo "odd number $counter" ; fi
done

【讨论】:

感谢大括号与括号对真/假的解释 关于第二个选项,也可以这样使用:if (( $some_var:+1 )); then ...,如果设置了变量,则将其转换为1【参考方案5】:

0 退出代码表示成功只是一个约定。 EXIT_SUCCESS 在几乎所有现代系统上都是 0。

编辑:

“为什么测试0和测试1都返回0(成功)?”

这是一个完全不同的问题。答案是,将单个参数传递给 test 总是会导致成功,除非该参数是空字符串 ("")。请参阅Open Group documentation。

【讨论】:

那为什么test 0test 1都返回0(成功)? @httpinterpret, test 不测试数值。它测试该字符串是否为空字符串。 man test 了解更多信息。【参考方案6】:

通常程序返回零表示成功,非零表示失败; false 返回 1 是因为它是一个方便的非零值,但通常任何非零值都意味着某种故障,并且许多程序会返回不同的非零值以指示不同的故障模式

【讨论】:

在没有任何解释(或明显原因)的情况下投反对票很糟糕,这样做的人很蹩脚,因为他们根本没有帮助社区。​​span> 不宽恕它......但它 他们的 2 点......和@MichaelMrozek.. 19.6k,它不会伤害太严重,哈哈。 @alexgray 这是两年前的事了;当时我有大约5k。我更好奇答案有什么问题,而不是代表【参考方案7】:

这是一个可以追溯到 Unix 早期的约定。

按照惯例,如果成功,所有系统调用都返回 0,否则返回非零,因为这样可以使用不同的数字来表示不同的失败原因。

Shell 遵循此约定,0 表示最后一个命令成功,否则为非零。同样,非零返回值对于输出错误消息也很方便:例如1:“脑死亡”,2:“无情”,以此类推。

【讨论】:

这就是答案。【参考方案8】:

AFAIK 这来自 C 约定,如果成功,您应该返回 0。 见:

man close

大多数 C (POSIX) api 都是这样构建的。 http://en.wikipedia.org/wiki/C_POSIX_library

【讨论】:

【参考方案9】:

您试图将真/假等同于成功/失败。

他们完全是两个,虽然起初很微妙,不同的二分法!

在 shell 脚本中,没有真/假之类的东西。 Shell '表达式' 不会被解释为真/假。相反,shell“表达式”是成功或失败的进程。

显然,一个进程可能由于多种原因而失败。因此,我们需要更大的一组代码来映射可能的故障。正整数可以解决问题。另一方面,如果该过程成功,这意味着它完全按照它应该做的事情做。由于只有一种方法可以做到这一点,因此我们只需要一个代码。 0 可以解决问题。

在 C 中,我们正在创建一个程序。在一个 shell 脚本中,我们运行了一堆程序来完成一些事情。

区别!

【讨论】:

令人困惑。键入“人 [”。它显示测试实用程序计算表达式,如果计算结果为真,则返回零(真)退出状态;否则返回 1(假)。如果没有表达式,test 也返回 1 (false)。【参考方案10】:

也许记住它的好方法是:

返回代码回答“答案是什么?”真(或其他结果)或假 退出代码回答“有什么问题?”退出代码或没有问题 (0)

【讨论】:

【参考方案11】:

虽然这个问题没有标记为 C 编程,而且大多数答案似乎间接解释了真正的原因,但我想我会回答,我希望 C 程序员会觉得有用。

恕我直言,歧义的真正来源是 C 标准。例如,在 C11 草案规范 (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) 部分“7.22.4.4 退出函数”与“7.18 布尔类型和值 ”部分中,EXIT_SUCCESS 为零,这是从 linux shell cmdline 运行的程序预计会在成功时返回,而 bool true 非零(#define'ed 为 1,但称其为非零更安全。为什么?请参阅 https://***.com/users/827263/keith-thompson 在 https://***.com/a/40009047/4726668 中的评论)。

引用 C11 草案规范 (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf):

7.22.4.4 退出函数

"

5 最后,控制权返回到宿主环境。如果 status 的值为 0 或 EXIT_SUCCESS,则返回实现定义的状态成功终止形式。如果 status 的值为 EXIT_FAILURE,则返回实现定义的状态不成功终止形式。否则返回的状态是实现定义的。

"

7.18 布尔类型和值

"

3 其余三个宏适用于#if 预处理指令。他们是

是的

展开成整数常数1,

展开成整数常量 0,并且

_ _bool_true_false_are_defined

展开成整数常量 1。

"

【讨论】:

以上是关于为啥在shell中0为真而假为1?的主要内容,如果未能解决你的问题,请参考以下文章

这是否可以检查swift数组中的所有值是否为真而不是逐个循环?

shell命令中的条件测试与判断语句

shell基础DAY7

shell 条件判断

shell条件判断

shell的if命令如何判断一个文件中所含指定字符串?