关于错误处理:Linux中是否有任何标准的退出状态代码?

Are there any standard exit status codes in Linux?

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

我已经看到分割错误通常会导致退出状态为11,但我不知道这是否只是我工作的约定(失败的应用程序都是内部的)或标准。

Linux中的进程是否有标准的退出代码?


第1部分:高级bash脚本指南

与往常一样,《高级bash脚本编写指南》提供了大量信息:(这在另一个答案中链接,但链接到一个非规范的URL。)

1: Catchall for general errors
2: Misuse of shell builtins (according to Bash documentation)
126: Command invoked cannot execute
127: "command not found"
128: Invalid argument to exit
128+n: Fatal error signal"n"
255: Exit status out of range (exit takes only integer args in the range 0 - 255)

第2部分:系统出口.h

absg引用了sysexits.h

Linux上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ 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 */


wait(2)&co.返回时,将返回码的8位和终止信号的8位混合到一个值中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#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
", 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
", WTERMSIG(status));
    /* prints:"second child died with 11" */
}

您如何确定退出状态?传统上,shell只存储8位返回代码,但如果进程异常终止,则设置高位。

1
2
3
4
5
6
7
$ sh -c 'exit 42'; echo $?
42
$ sh -c 'kill -SEGV $$'; echo $?
Segmentation fault
139
$ expr 139 - 128
11

如果您看到的不是这个,那么程序可能有一个SIGSEGV信号处理程序,它通常调用exit,因此它实际上不会被信号杀死。(程序可以选择处理除SIGKILLSIGSTOP之外的任何信号。)


'1'>>>一般错误的catchall

'2'>>>误用shell内置项(根据bash文档)

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

'127'>>>"找不到命令"

'128'>>>要退出的参数无效

'128+N'>>>致命错误信号"N"

'130'>>>由Control-C终止的脚本

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

这是给巴什的。但是,对于其他应用程序,有不同的退出代码。


以前的回答都没有正确描述退出状态2。与它们所宣称的相反,状态2是命令行实用程序在不正确地调用时实际返回的内容。(是的,一个答案可能有9年的历史,有数百张赞成票,但仍然是错误的。)

以下是正常终止的真实、长期的退出状态约定,即不通过信号:

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

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

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

笔记

  • 有几个答案将退出状态2定义为"bash内置的误用"。这仅在bash(或bash脚本)以状态2退出时适用。将其视为不正确使用错误的特殊情况。

  • 在最常见的回答中提到的sysexits.h中,exit status EX_USAGE(命令行使用错误)被定义为64。但这并不能反映实际情况:我不知道有任何常见的Unix实用程序在错误调用时返回64(欢迎使用示例)。仔细阅读源代码可以发现,sysexits.h是有抱负的,而不是真实用法的反映:


    1
    2
    3
    4
    5
    6
    7
     *    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.

    换句话说,这些定义并不反映当时(1993年)的惯例,而是有意与之不相容。更可惜的是。


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

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

    segfault上的11很有趣,因为11是内核在segfault事件中用来终止进程的信号号。很可能在内核或shell中有某种机制将其转换为退出代码。


    sysexits.h有一个标准退出代码列表。它至少可以追溯到1993年,一些大型项目如postfix使用它,所以我认为这是一个可行的方法。

    从OpenBSD手册页:

    According to style(9), it is not good practice to call exit(3) with arbi-
    trary values to indicate a failure condition when ending a program. In-
    stead, the pre-defined exit codes from sysexits should be used, so the
    caller of the process can get a rough estimation about the failure class
    without looking up the source code.


    对于第一个近似值,0是成功的,非零是失败,1是一般的失败,任何大于1的都是特定的失败。除了"假"和"测试"这两个小的异常,这两个都是为成功而设计的,还有一些我发现的其他异常。

    更现实地说,0表示成功或失败,1表示一般失败或成功,2表示一般失败,如果1和0都用于成功,但也可能是成功。

    如果比较的文件相同,diff命令给出0;如果文件不同,则给出1;如果二进制文件不同,则给出2。2也意味着失败。less命令给出1表示失败,除非您未能提供参数,在这种情况下,尽管失败,它还是退出0。

    more命令和spell命令给出1表示失败,除非失败是由于权限被拒绝、文件不存在或试图读取目录造成的。在任何这些情况下,尽管失败,它们还是退出0。

    然后expr命令给出1表示成功,除非输出为空字符串或零,在这种情况下,0表示成功。2和3是故障。

    有时成功或失败是模棱两可的。当grep找不到模式时,它退出1,但它退出2是因为真正的失败(如权限被拒绝)。当klist找不到一张罚单时,它也会退出1,尽管这实际上并不比grep找不到模式或者当你是一个空目录时更失败。

    因此,不幸的是,Unix的强大功能似乎没有强制执行任何逻辑规则集,即使是在非常常用的可执行文件上。


    程序返回一个16位的退出代码。如果用信号终止程序,那么高阶字节包含所用的信号,否则低阶字节是程序员返回的退出状态。

    如何将该退出代码分配给状态变量$?然后到了外壳。bash保持较低的7位状态,然后使用128+(信号nr)指示信号。

    程序的唯一"标准"约定是0表示成功,非0表示错误。使用的另一个约定是在出错时返回errno。


    标准的Unix出口代码是由sysexits.h定义的,正如另一个海报所提到的。Poco等可移植库使用相同的退出代码-以下是它们的列表:

    http://pocoproject.org/docs/poco.util.application.html 16218

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


    当Linux返回0时,意味着成功。任何其他的都意味着失败,每个程序都有自己的退出代码,所以列出所有这些代码会花很长时间…!

    关于11个错误代码,它实际上是分段错误号,主要意味着程序访问了一个未分配的内存位置。