Linux中是否有任何标准的退出状态码?

381

如果在Linux中进程的退出状态为0,则认为该进程已经正确完成。

我发现,分段错误常常会导致退出状态为11,但我不知道这是否只是我们公司的惯例(所有失败的应用程序都是内部的),还是一个标准。

在Linux中,进程的退出码是否有标准化的规定?


6
如果你正在寻找系统函数返回的“系统错误号”,请查看errno - marinara
12个回答

388

第一部分:高级Bash脚本指南

和往常一样,高级Bash脚本指南很棒的信息: (这个链接在另一个答案中提到过,但是链接不是官方的。)

1:一般错误
2:使用了shell内置命令(根据Bash文档)
126:无法执行调用的命令
127:"命令未找到"
128:退出时参数无效
128+n:致命错误信号“n”
255:退出状态超出范围(exit只接受0-255之间的整数参数)

第二部分:sysexits.h

ABSG引用了sysexits.h

在Linux上:

$ find /usr -name sysexits.h
/usr/include/sysexits.h
$ cat /usr/include/sysexits.h

/*
 * Copyright (c) 1987, 1993
 *  The Regents of the University of California.  All rights reserved.

 (A whole bunch of text left out.)

#define EX_OK           0       /* successful termination */
#define EX__BASE        64      /* base value for error messages */
#define EX_USAGE        64      /* command line usage error */
#define EX_DATAERR      65      /* data format error */
#define EX_NOINPUT      66      /* cannot open input */    
#define EX_NOUSER       67      /* addressee unknown */    
#define EX_NOHOST       68      /* host name unknown */
#define EX_UNAVAILABLE  69      /* service unavailable */
#define EX_SOFTWARE     70      /* internal software error */
#define EX_OSERR        71      /* system error (e.g., can't fork) */
#define EX_OSFILE       72      /* critical OS file missing */
#define EX_CANTCREAT    73      /* can't create (user) output file */
#define EX_IOERR        74      /* input/output error */
#define EX_TEMPFAIL     75      /* temp failure; user is invited to retry */
#define EX_PROTOCOL     76      /* remote error in protocol */
#define EX_NOPERM       77      /* permission denied */
#define EX_CONFIG       78      /* configuration error */

#define EX__MAX 78      /* maximum listed value */

5
请注意,在某些版本的Unix中,一些命令使用退出状态2来指示其他内容。例如,许多grep实现使用退出状态2表示错误,并使用退出状态1表示未找到任何选定行。 - NamshubWriter
5
在BSD系统中,有一个man手册总结了来自sysexits.h的信息:man sysexits - georgebrock
10
@NamshubWriter所说的。Exit status 2 是Unix工具中普遍用于错误命令行用法的返回值,不仅限于“某些Unix版本”,而是普遍适用的。此回答中显示的标头并不反映实际惯例,无论是现在还是1987年撰写时。 - alexis
3
ABS并不是“伟大”的。请阅读相关主题;批评并不难找到。 - tripleee
但是 sysexits.h 的实际官方源代码在哪里呢?每个人都参考的man页面只是散文。例如,它引用了 EX_OK,但并没有像其他代码那样规范地定义它。是否还有其他缺失的代码? - Garret Wilson
2
@Schof,...Wooledge BashGuide的撰写是因为freenode的#bash频道的居民们厌倦了试图纠正其他人从ABS学到的错误做法和明显不正确的信息。(一些明显的不准确之处已经得到了清理,但它仍然充斥着糟糕的实践例子,而且它的维护者完全不愿意为了回应批评而辩护或更新内容)。 - Charles Duffy

95

wait(2)和相关函数的返回值中,终止进程的信号编号和返回码(exit code)会被混合在一个8位字节中。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>

int main() {
    int status;

    pid_t child = fork();
    if (child <= 0)
        exit(42);
    waitpid(child, &status, 0);
    if (WIFEXITED(status))
        printf("first child exited with %u\n", WEXITSTATUS(status));
    /* prints: "first child exited with 42" */

    child = fork();
    if (child <= 0)
        kill(getpid(), SIGSEGV);
    waitpid(child, &status, 0);
    if (WIFSIGNALED(status))
        printf("second child died with %u\n", WTERMSIG(status));
    /* prints: "second child died with 11" */
}
你是如何确定退出状态的?传统上,Shell 只存储 8 位返回码,但如果进程异常终止,则设置高位。如果您看到的不是这样的结果,则可能程序有SIGSEGV信号处理程序,该处理程序然后正常调用退出函数(exit),因此它实际上并没有被信号杀死。(除了SIGKILL和SIGSTOP信号外,程序可以选择处理任何信号。)

12
鉴于现在问题的形式,这个答案似乎不是最有用的(也就是不被接受的)。 - David J.

93

之前的回答没有正确描述退出状态2。与其所声称的相反,当命令行工具被错误调用时,状态2实际上是返回的内容。(是的,一个回答可能有九年历史,拥有数百个赞,但仍然是错的。)

这里是正常终止(即非信号终止)的真正、长期存在的退出状态约定:

  • 退出状态0:成功
  • 退出状态1:“失败”,由程序定义
  • 退出状态2:命令行使用错误

例如,如果比较文件并且它们相同,则diff返回0;如果它们不同,则返回1。按照长期惯例,Unix程序在被错误调用(未知选项、错误数量的参数等)时返回退出状态2。例如,diff -Ngrep -Ydiff a b c都会导致$?设置为2。这是自20世纪70年代Unix早期以来一直实践的做法。

被接受的答案解释了当命令由信号终止时会发生什么。简而言之,因未捕获信号而终止导致退出状态为128+[<signal number>。例如,由SIGINT信号2)终止会导致退出状态为130。

注意事项

  1. Several answers define exit status 2 as "Misuse of bash builtins". This applies only when bash (or a bash script) exits with status 2. Consider it a special case of incorrect usage error.

  2. In sysexits.h, mentioned in the most popular answer, exit status EX_USAGE ("command line usage error") is defined to be 64. But this does not reflect reality: I am not aware of any common Unix utility that returns 64 on incorrect invocation (examples welcome). Careful reading of the source code reveals that sysexits.h is aspirational, rather than a reflection of true usage:

     *    This include file attempts to categorize possible error
     *    exit statuses for system programs, notably delivermail
     *    and the Berkeley network.
    
     *    Error numbers begin at EX__BASE [64] to reduce the possibility of 
     *    clashing with oth­er exit statuses that random programs may 
     *    already return. 
    

    In other words, these definitions do not reflect the common practice at the time (1993) but were intentionally incompatible with it. More's the pity.


2
如果程序捕获了SIGINT信号,进行清理并退出,那么状态应该是对于该程序有意义的。例如,more会重置终端模式并以状态0退出(您可以尝试一下)。 - alexis
3
这个答案暗示了比实际情况更高的标准化程度。对于值2的含义,没有适当的标准化,实际应用也是非常混乱的。确实有许多工具会返回2表示不正确的使用,但“不正确的使用”具体指什么并没有明确定义,而且许多其他工具也不遵循这种惯例。 - tripleee
2
“工具”也无法定义!:-) 当然,任何人都可以编写命令行程序,它可以返回任何东西,但是比 Linux 更久远的旧式“Unix命令行实用程序”,或者 GNU核心实用程序的内容,在这方面相当一致。如果您认为不同,请列出一些不使用状态2的工具。此外,“不当使用”是您的术语(我同意这是一个含糊的术语);我写了“命令行使用错误”,这是非常具体的:不存在或不兼容的选项,非选项参数数量错误等。 - alexis
1
在我的系统上,mkdir --xyzmv --xyzcp --xyz都返回退出码1。另一方面,bash --xyzcd --xyz返回退出码2。 - rrrrrrrrrrrrrrrr
1
@alexis GNU/Linux。标准GNU工具。 - rrrrrrrrrrrrrrrr
显示剩余14条评论

78

'1': 通用错误的捕获

'2': 使用了shell内置命令(根据Bash文档)

'126': 无法执行调用的命令

'127': “找不到命令”

'128': 退出操作的参数无效

'128+n': 致命错误信号“n”

'130': 脚本被Ctrl + C终止

'255': 退出状态超出范围

这适用于Bash。然而,对于其他应用程序,有不同的退出代码。


1
看起来你们两个在同一分钟内回答了。天必须非常快才能看到你的链接并将它们粘贴进去。 - Nathan Fellman
9
请注意,“按下Ctrl+C会产生130”的说法与信号n的“128+n”的规律是一致的;按下Ctrl+C会产生SIGINT信号,它是信号2。 - Jonathan Leffler
4
这似乎是从ABS抄袭而来的,但没有注明出处。(我们可以看出这一点,因为ABS中包含了不正确或至少是误导性的信息。) - tripleee
6
这些是保留的退出代码,根据高级Bash脚本指南。这意味着这些值应该避免用于用户指定的退出参数。 - ingyhere

24

除了0表示成功之外,没有标准的退出代码,非零并不一定意味着失败。

头文件stdlib.h确实将EXIT_FAILURE定义为1,EXIT_SUCCESS定义为0,但仅此而已。

段错误中的11很有趣,因为11是内核在发生段错误时用于终止进程的信号编号。可能有一些机制,无论是在内核还是在 shell 中,将其转换为退出代码。


声称没有标准退出代码是不正确的。wait(2)的man页面列出了用于解码退出状态的宏,头文件sysexits.h除了01之外还标准化了其他整数常量。 - ruief
wait(2)中的宏是特定于来自wait的“wstatus”的额外内容。请注意,其中一个宏被定义为“WEXITSTATUS(wstatus)-返回子进程的退出状态。”那就是我理解我们要谈论的状态。sysexits.h很有趣,但我认为它没有广泛的使用。所以是的,它可能是一种标准,并值得记住新工作......但我不会假设任何给定的程序符合它。 - Chris Arguin

23

头文件sysexits.h包含一组标准的退出码。看起来至少可以追溯到1993年,一些大项目像Postfix使用它,所以我想这是正确的方式。

来自OpenBSD手册的引用:

根据style(9)的说明,在结束程序时不建议使用任意值调用exit(3)来指示失败条件。应该使用sysexits中预定义的退出码,这样进程的调用者就可以得到有关失败类型的大致估计,而无需查找源代码。

14

就近似而言,0表示成功,非零表示失败,1表示一般性的失败,大于1则表示特定的失败。除了false和test这两个显然设计为成功返回1之外,我还发现了几个其他的例外。

更实际地说,0意味着成功或者可能是失败,1意味着一般性的失败或者可能是成功,2意味着如果0和1都用于表示成功,则表示一般性的失败,但同时也可能表示成功。

diff命令比较文件后,如果相同则返回0,不同则返回1,二进制文件不同则返回2。2也表示失败。less命令在没有提供参数的情况下返回0,即使运行失败,否则将返回1。 more命令和spell命令返回1表示失败,除非失败是因为权限被拒绝、文件不存在或试图读取目录。在任何这些情况下,它们将尽管失败但退出0。最后,expr命令返回1表示成功,除非输出为空字符串或零,在这种情况下,0表示成功。2和3表示失败。

还有一些成功或失败不明确的情况。当 grep 无法找到模式时,它会退出1,但对于真正的失败(如权限被拒绝),它会退出2。当 klist 无法找到票证时,也会退出1,尽管这并不比 grep 无法找到模式或您ls一个空目录时更加失败。

因此,不幸的是,Unix的权威人士似乎没有强制执行任何逻辑规则,即使在非常常用的可执行文件上也是如此。


我也正想指出diff的行为。wget也有详细的错误信息(例如,6表示身份验证失败),但是它们使用1 = 通用错误,2..n = 特定错误。 - PypeBros

5
程序返回一个16位的退出码。如果程序被信号杀死,则高位字节包含使用的信号,否则低位字节是程序员返回的退出状态。
如何将退出码分配给状态变量$?这取决于shell。Bash保留状态的较低7位,然后使用128 +(信号nr)表示信号。
程序的唯一“标准”约定是成功为0,错误为非零。另一个常用的约定是在出错时返回errno。

1
其他人都说是8位,请问你能提供一下来源吗? - Tom Hale
1
被接受的答案和我的说的一样,即8位是状态,8位是信号。我写这个答案已经很久了,所以我记不得我用的来源是什么了 :-). - Dean Povey

4

标准Unix退出代码由sysexits.h定义,如David提到的。 这些相同的退出代码被可移植库(如Poco)使用 - 这是它们的列表:

Poco :: Util :: Application类,ExitCode

信号11是SIGSEGV(段错误)信号,与返回代码不同。该信号由内核响应错误页面访问而生成,导致程序终止。信号列表可以在信号手册中找到(运行“man signal”)。


源代码中写着“4.3BSD <sysexits.h>头文件”,这是POSIX的一部分吗? - Peter Mortensen

1

有些是约定,但一些其他保留的内容是POSIX标准的一部分。

126 -- 找到要执行的文件,但它不是可执行实用程序。

127 -- 未找到要执行的实用程序。

>128 -- 命令被信号中断。

请参阅 man 1p exitRATIONALE 部分。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接