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

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

考虑:(使用 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)

……这些都有道理,但是……

cat real-files

# Returns: 0

cat not-files

# Returns: 1 (No such file or directory)

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

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

我认识到grep可能会产生三个结果(上面各有),但我认为ls只会有两个结果cat。因此,两种可能的结果不能成为原因,cat因为ls.

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


接受答案后,我希望得到一个对原始问题进行扩展的答案,因为这是 Linux/BASH,而不是 UNIX 正确的:UNIX(即在 Mac 上)会做相同的事情或类似的事情吗?

答案1

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

这是 BASH 代码中的问题吗?

不,cat是完全独立的二进制应用程序,与bash.在某些 shell 配置中,如所指出的斯蒂芬·查泽拉斯cat可以是内置的,但即使如此,应用程序的返回状态也完全独立于该应用程序是否与 shell 相关。

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

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


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

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

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

这意味着只有状态 0 具有指定含义,该含义被指定给 EXIT_SUCCESS,如中指定的标准库文件眼镜。但这是 POSIX 规范,Linux 规范相比如何?嗯,差不多是一样的:Linux退出(3)手册甚至没有指定可能的值。

另请注意,它说的是“可能是”而不是“应该是”,意思是应用程序并不绝对需要以特定值退出,即使出现错误。您的应用程序可能会遇到错误或失败,但退出时仍返回 0。

然而,POSIX 规范对于每个便携式应用程序确实指定了 EXIT STATUS 部分,即具体到每个应用程序。再次强调,除了 0 表示成功以及非 0 表示其他任何模式之外,没有任何模式。例如,POSIX 猫规格要求:

The following exit values shall be returned:

0    All input files were output successfully.

>0   An error occurred.

为了grep我们有:

The following exit values shall be returned:

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

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


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

事实上,如果我们考虑错误号 甚至不需要分配特定的整数值并且取决于实现。因此,很可能存在类似 Unix 的实现,将 ENOENT 视为整数 2。但同样,这完全不相关,因为退出状态和 errno 是不同的东西。

综上所述

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

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

答案2

GNUcoreutils文档cat

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

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

的手册页grep

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

以及以下的手册页ls

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

您的结果与文档一致。

答案3

程序的退出状态必须遵循一些规则,除了这些规则之外,还有一些常见的约定。这些约定都与导致程序退出的低级错误无关。可以编写一个程序,该程序退出时会显示特定的错误代码(因为文件不存在而决定退出),如果由于访问文件的权限被拒绝而决定退出,则会显示不同的错误代码,并且会显示不同的错误代码如果路径的目录组件结果是非目录,等等,但这将是非常不寻常且难以安排的。

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

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

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

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

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

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

一些程序为不同的错误定义了不同的错误代码,例如发送邮件以及其他一些与邮件相关的程序(在中定义的值sysexits.h),同步,卷曲,获取

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

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

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

^ waitidint通过授予对全部价值的访问权限infop->si_status
²通过$?或其他方式。例如,如果exit256是一个以 退出的程序exit(256),则 shell 命令if exit256; then echo "exited with 0"; fi将打印“exited with 0”。

答案4

根据您的系统,cat可能是内置的 shell 或单独的二进制文件。要查看它是什么,您可以运行

$ command -V cat

另外,GNU 的行为cat确实是正确的,引用 POSIX猫(1)

退出状态

应返回以下退出值:

0
所有输入文件均已成功输出。

>0
发生错误。

errno 和退出状态之间的关系仅仅是巧合,因为 errno 不需要位于退出代码的低 8 位范围内(POSIX 要求传递这些代码)。

不过,兼容 POSIX '01 的 SunOS 5.10 确实返回 2(XPG3 和 POSIX '01 之间没有标准显然改变了该工具的行为):

$ PATH=`getconf -v POSIX.1-2001 PATH`
$ export PATH
$ command -v cat
/usr/bin/cat
$ cat nosuchfile
cat: cannot open nosuchfile
$ echo $?
2

ENOENT这确实也发生在 Solaris 上:

$ grep ENOENT /usr/include/sys/errno.h
#define ENOENT  2       /* No such file or directory            */

然而,联机帮助页仅记录 >2。

相关内容