为什么失败的猫返回 1,而其他失败的猫返回 2?

Jes*_*ele 12 cat exit-status

考虑:(使用 Linux/BASH,不确定 UNIX 是否正确)

在争论一个不存在的文件时,我预计会出现 2 个错误...

grep "i am here" real-file

# Returns: 0 (via: echo $?)

grep "i am not here" real-file

# Returns: 1

grep "i am not here" not-a-file

# Returns: 2 (No such file or directory)

ls real-files

# Returns: 0

ls not-files

# Returns: 2 (No such file or directory)
Run Code Online (Sandbox Code Playgroud)

……这些都说得通,但是……

cat real-files

# Returns: 0

cat not-files

# Returns: 1 (No such file or directory)
Run Code Online (Sandbox Code Playgroud)

...“没有这样的文件或目录”不应该是退出状态为 2 的 STDERR 吗?

状态 2 随附grepls非文件,但cat返回 1 并带有相同的错误消息。

我知道这grep可能有三个结果(每个都在上面),但我认为ls只有两个结果cat。因此,两种可能的结果都不是原因,cat因为事实并非如此ls.

这是 BASH 代码中的问题吗?我们需要打电话给莱纳斯和理查德吗?如果这是正确的,请帮助我理解原因。


接受答案后,我希望得到一个扩展原始问题的答案,因为这是 Linux/BASH,而不是 UNIX

Ser*_*nyy 19

让我们从下到上解决一些部分,并首先摆脱不重要的部分:

这是 BASH 代码中的问题吗?

不,cat是完全独立的二进制应用程序,与bash. 在某些 shell 配置中,正如Stephane Chazelas所指出的,cat可以是内置的,但即使如此,应用程序的返回状态也与该应用程序是否与 shell 相关完全分开。

我们需要打电话给莱纳斯和理查德吗?如果这是正确的,请帮助我理解原因。

不,这不是问题,Linus 和 Richard 在这里完全没有关系。好吧,更正:除非他们有一天宣布这exit()和 errno 绝对必须相关,并且出于某种奇怪的原因,我们必须遵循他们的所有技术决定。


两个应用程序都返回不同的退出状态是完全可以的,因为POSIX 规范没有明确的限制或分配说“这个非零退出状态应该意味着这个和那个”。

退出系统调用状态的POSIX 文档

status 的值可以是 0、EXIT_SUCCESS、EXIT_FAILURE 或任何其他值,但只有最低的 8 位(即 status & 0377)可用于等待的父进程。

这意味着只有状态 0 分配了含义,如stdlib.h规范中指定的那样分配给 EXIT_SUCCESS 。但这是 POSIX 规范,Linux 规范如何比较?嗯,大致相同:Linux exit(3)手册甚至没有指定可能的值。

还要注意,它说“可能是”而不是“应该是”,从某种意义上说,应用程序并不是绝对需要以特定值退出,即使在出现错误的情况下,。您的应用程序可能会遇到错误或失败,并且在退出时仍会返回 0。

但是,每个便携式应用程序的POSIX 规范确实指定了 EXIT STATUS 部分,这是特定于每个应用程序的。同样,除了成功的 0 和其他任何非零的模式之外,没有其他模式。例如,POSIX cat 规范要求:

The following exit values shall be returned:

0    All input files were output successfully.

>0   An error occurred.
Run Code Online (Sandbox Code Playgroud)

对于grep,我们有:

The following exit values shall be returned:

 0    One or more lines were selected.
 1    No lines were selected.
>1    An error occurred.

Run Code Online (Sandbox Code Playgroud)

在 Linux 上下文中,cat(1)没有明确说明这些状态值,但GNU 文档会grep(1)手册提到使用 2 的退出代码,但即便如此,也承认 POSIX 实现只需要大于零的错误条件,并敦促“......为了可移植性,使用测试此一般条件的逻辑而不是与 2 严格相等。”


值得一提的是,在某些情况下,假设exit()状态值等于errno值。到目前为止,我找不到任何表明 POSIX 需要的文档或参考资料。事实上,情况恰恰相反。请注意,POSIX退出规范和 Linux exit(3) 手册页没有明确说明退出状态必须以某种方式匹配 errno。所以在 GNU 中返回值为 2 的事实grep中与 ENOENT 错误值 2 匹配纯属巧合。

事实上,如果我们考虑errno.h 甚至不需要分配特定的整数值,而是依赖于实现。因此,很可能存在将 ENOENT 视为整数 2 的类 Unix 实现。但同样 - 这完全无关,因为退出状态和 errno 是不同的东西。

总之

cat返回不同的退出代码这一事实grep是适当的并且与这些应用程序的规范一致。退出代码的含义不是固定的,取决于每个单独的应用程序(除非它是 POSIX 应用程序,如catgrep,在这种情况下,为了可移植性,它们应该遵循)。

引用GNU OS 文档:“最常见的约定是 0 表示成功,1 表示失败。执行比较的程序使用不同的约定:它们使用状态 1 表示不匹配,使用状态 2 表示无法比较。您的如果现有约定对其有意义,则程序应遵循现有约定。”

  • `ksh93` 有一个内置的 `cat`(如果 `/opt/ast/bin` 在 `$PATH` 中位于 `/bin` 之前或在 `builtin cat` 之后,则启用)。`cat` 也在busybox `sh` 中内置(有点)。bash 还附带了一个可加载的 `cat` 内置函数。一些发行版使其在`bash-builtins` 包中可用。 (5认同)
  • 人们应该始终将 `errno` 的“命名空间”或“值空间”与退出状态完全、完全和完全分开。它们是不同的事物,它们的价值具有不同的含义,它们完全没有关联。 (3认同)
  • 值得指出的是 `errno` 代码_可能_大于 255。它们中的任何一个都不能通过 `exit()` 和 `waitpid()` 传递而不会丢失数据。(在我输入的计算机上,所有的 errno 代码都安全地低于 255,但是 ISO C 和 POSIX 对 errno 代码值的唯一约束是它们必须是正数并且适合一个 `int` ;见 https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html。) (3认同)
  • “退出状态通常与 errno 相对应”:需要引用。 (2认同)
  • “退出状态与 errno 相对应是很典型的”——不,相反,这是不常见的,而且几乎可以肯定是一个错误。(你可以告诉 Larry 我是这么说的。)“Linux 上下文中的一些 GNU 工具在没有文件时返回退出状态 2”——不,`ls` 返回 2 因为这是它为任何错误返回的错误代码,事实上`ENOENT` 的值为 2,`ls` 遇到的常见错误只是一个巧合。 (2认同)

Fre*_*ddy 18

在GNU的coreutils文件cat

退出状态为零表示成功,非零值表示失败。

...非零退出状态表示失败,仅此而已。

的手册页grep

通常,如果选择了一行,退出状态为 0,如果没有选择任何行,则退出状态为 1,如果发生错误,则退出状态为 2。但是,如果使用-qor--quiet--silent选择了一行,即使发生错误,退出状态也是 0。

和手册页ls

退出状态:
0 表示正常,
1 表示小问题(例如,无法访问子目录),
2 表示严重问题(例如,无法访问命令行参数)。

您的结果与文档一致。

  • @UncleBilly 这个问题没有用 linux 标记(也没有用 posix 标记),但在其文本中有“Linux”,所以我假设使用了 GNU 实用程序(它们可能是,也可能不是),至少退出代码与行为匹配GNU 工具。 (4认同)
  • 您应该指向并引用标准(即 POSIX)规范而不是 GNU 文档。原始答案没有 GNU、coreutils 或 linux 标签。例如,标准并没有说 grep 的退出状态在出错的情况下应该正好是 2,而是大于 1。而且标准也没有要求 ls 的退出状态来区分“小问题”和“严重问题”麻烦”。不,我不会再写一个答案——我只会贬低你的答案;-) (3认同)
  • @UncleBilly,最初的问题是“使用 Linux”......话虽如此,GNU 工具的假设是很自然的,并解释了他们得到的结果,所以你可能想稍微调整一下那个 downvote-trigger。 (3认同)
  • @JesseSteele,一般来说,不要过多地阅读 == 0 和 != 0 以外的退出代码,除非相关工具的文档说明存在差异。如您所见,它们并不像人们希望的那样一致。(虽然 >= 127 通常由 shell 生成,用于错误和由信号终止的程序。) (2认同)
  • @UncleBilly Linux 标签在这里不合适。该选项卡用于询问有关 Linux 系统的内核和 interlans 的问题,而不仅仅是表明问题与 Linux 有关。请花时间学习当地的标签习俗,然后再去指导他人。 (2认同)

Gil*_*il' 9

程序的退出状态必须遵循一些规则,除了这些规则之外,还有一些通用约定。这些约定都与导致程序退出的低级错误无关。有可能编写一个程序退出时出现某个错误代码,它决定退出是因为文件不存在,如果它决定退出,因为它被拒绝访问文件的权限,以及不同的错误代码,这是可能的如果路径的目录组件结果是非目录,等等,但那将是非常不寻常且难以安排的。

程序的退出状态是一个整数值。在POSIX 系统上,该值的类型为int,通常范围从 -2 31到 2 31 +1。然而,由于几个原因,这个范围的大部分在实践中是不可用的。首先,由于历史原因,大多数允许程序观察其子进程退出状态的接口只返回退出状态的低 8 位,即 0 到 255 之间的值。这包括系统函数waitwaitpid¹ 作为以及shell²中的退出状态。因此,对于几乎所有的意图和目的,退出状态都是一个 8 位值。

值 0 被视为成功,其他所有值都被视为失败。这是外壳,在外壳布尔运算符ifwhile构造涉及真/假的概念和其他任何考虑退出状态0是真实的和任何其它身份是假的。情况也是如此make,其中非零退出状态会导致构建停止并显示错误消息和错误状态。你可以质疑它是否是一个约定(因为程序的作者在技术上可以返回它想要的任何状态,而“成功”和“失败”无论如何都没有正式定义),但实际上,一个以状态 0 退出的程序被认为是成功的,而以另一个状态 (1-255) 退出的程序被认为是失败的。

外壳程序的另一个特别限制范围的功能是外壳程序中的退出状态(通过 观察$?)编码其他信息:

  • 126 表示命令名称是一个现有文件,不可执行。
  • 127 表示找不到命令名称。
  • 128+ N传统上(今天仍然在大多数 shell 中)表示命令以信号N退出。一些炮弹使用不同的范围,总是超过 128。

因此,在实践中,程序不能有效地使用超过 125 的退出状态。这留下值 1-125 来表示不同的错误。

有一种普遍但远非普遍的惯例,即较大的值被视为“更糟”的失败。特别是,对于诸如 之类的搜索命令grep,1 表示“未找到”,2 或更多表示阻止搜索的某些错误(例如,未找到文件,与找到但不包含搜索字符串的文件相反)。类似地,比较命令如cmpdiff以状态 0 表示“相同的文件”,1 表示“不同的文件”和 2 或更多表示“由于错误而无法完成比较”。

一些程序为不同的错误定义了不同的错误代码,例如sendmail和其他一些与邮件相关的程序(在 中定义的值sysexits.h)、rsynccurlwget

到目前为止,错误代码最常见的约定是 0 表示成功,1 表示失败。C 和 C++ 编程语言定义EXIT_FAILURE为退出状态代码,用于在没有特殊原因选择特定值时报告失败,并且EXIT_FAILURE在大多数系统上为 1。

诸如“没有这样的文件或目录”、“权限被拒绝”、“不是目录”等错误的背后都有一个数字编码:它们是errno值,由系统函数返回以指示出了什么问题。Errno 值通常不能用作程序的退出状态。他们对出错的细节进行编码,而不是对特定程序意味着什么。例如wget的退出状态区分“选项中的解析错误”(通常没有底层系统错误)、“本地输入/输出错误”(不管底层系统错误)、“网络故障”(这将在很大程度上与本地共享相同的系统错误I/O) 等。知道 wget 是由于网络错误还是本地文件错误而失败比知道它是否由于管道损坏(写入管道或关闭网络套接字上的连接?)或权限错误(无法读取配置文件,或本地策略拒绝网络访问?)。

返回状态遵循 errno 值的情况有些罕见。由于 Perldie函数的工作方式,它确实会发生,尤其是对于 Perl 脚本。但这是一个坏主意,不仅因为正如我上面提到的,errno 值很少是信息中最有用的部分,而且主要是因为 errno 值没有理由在 1-125 的范围内。幸运的是,我不知道任何 errno 值在 1-255 范围之外的系统,因此至少exit(errno)(或 Perl 的die)不会以 256 的倍数退出,正如我们在上面看到的那样,这表示成功。但是在 Linux 上,例如,它们确实达到了 126,并且一个程序以exit(errno)errno == ERFKILL (“由于 RF-kill 导致无法操作”)将无法与 shell 与因 SIGILL(非法指令)而死亡的程序区分开来。

¹授予通过. ²通过或其他方式。例如,如果是一个以 退出的程序,shell 命令会打印“以 0 退出”。 waitidintinfop->si_status
$?exit256exit(256)if exit256; then echo "exited with 0"; fi

  • *128+N* 不是 POSIX 要求,有些 shell 需要 256+N 或 384+N。一些非 POSIX shell 以其他方式对信号进行编码。另请参阅[进程终止时的默认退出代码?](//unix.stackexchange.com/a/99134) 和[是否可以确定上次运行的应用程序接收到的信号?](//unix.stackexchange.com/ a/484698)(也与此处相关:[Linux 中退出代码的最小值和最大值是多少?](//unix.stackexchange.com/q/418784)) (2认同)