什么是好的Linux退出错误代码策略?

时间:2008-12-26 14:40:55

标签: linux unix error-handling

我有几个独立的可执行Perl,PHP CLI脚本和C ++程序,我需要开发出口错误代码策略。这些程序由其他程序使用我创建的包装类调用,以便在PHP中使用exec()。所以,我将能够得到一个错误代码。根据该错误代码,调用脚本将需要执行某些操作。

我做了一些研究,似乎1-254(或者只是1-127)范围内的任何内容都可以是用户定义的错误代码的公平游戏。

我只是想知道其他人在这种情况下是如何处理错误处理的。

5 个答案:

答案 0 :(得分:8)

唯一的约定是您为成功返回0,对于错误则返回零以外的值。大多数知名的unix程序记录了他们可以返回的各种返回代码,您也应该如此。尝试为任意程序可能返回的所有可能错误代码制作一个通用列表没有多大意义,否则你最终会得到tens of thousands of them like some other OS's,即便如此,它也不会总是覆盖要返回的特定错误类型。

所以请保持一致,并确保记录您决定使用的任何方案。

答案 1 :(得分:6)

1-127是可用范围。任何超过127的东西都应该是“异常”退出 - 由信号终止。

当你在它时,考虑使用stdout而不是退出代码。退出代码按传统用于表示成功,失败,并且可能是另一个状态。而不是使用退出代码,尝试使用stdout expr和wc使用它。然后,您可以在调用者中使用反引号或类似内容来提取结果。

答案 2 :(得分:4)

unix宣言声明 -

  

出错时尽快退出

或类似的东西

答案 3 :(得分:2)

不要尝试将 编码到退出值中更多含义:详细的状态和错误报告应该像Arkadiy建议的那样转到stdout / stderr。

但是,我发现在退出值中只表示少数几个状态非常有用,使用二进制数字对它们进行编码。例如,假设您有以下设计意义:

0000 : 0 (no error)
0001 : 1 (error)
0010 : 2 (I/O error)
0100 : 4 (user input error)
1000 : 8 (permission error)

然后,用户输入错误的返回值为5(4 + 1),而没有写入权限的日志文件的返回值可能为11(8 + 2 + 1)。由于不同的含义在返回值中独立编码,您可以通过检查设置的位来轻松查看发生的情况。

作为一种特殊情况,要查看是否存在错误,您可以使用1返回代码。

通过这样做,您可以以简单明了的方式在返回代码中编码几个不同的东西。我只使用它来做出简单的决定,例如“应该重新启动进程”,“返回值和相关日志是否需要发送给管理员”等等。任何详细的诊断信息都应该转到日志或stdout / stderr。

答案 4 :(得分:1)

正常退出状态从0到255(有关原因的讨论,请参阅Exit codes bigger than 255 posssible)。通常,状态0表示成功;其他任何东西都是实现定义的错误。我知道一个程序通过退出状态报告DBMS服务器的状态;这是实现定义的退出状态的特例。请注意,您可以定义程序状态的实现。

我不能把它变成300个字符;否则它会对@Arkadiy的回答发表评论。

Arkadiy是正确的,在退出状态字的一部分中,除零之外的值表示终止进程的信号,第8位通常表示核心转储,但退出状态的该部分与主0不同.255状态。然而,当过程因信号而死亡时,外壳(无论是哪个外壳)都会出现问题。在8位值中存在16位数据,这总是很棘手。 shell的作用似乎是取信号并向其添加128。因此,如果进程因中断(信号编号2,SIGINT)而死亡,则shell将退出状态报告为130.但是,内核报告状态为0x0002; shell修改了内核报告的内容。

以下C代码演示了这一点。有两个程序

  • suicide使用您选择的信号杀死自己(默认中断)。
  • exitstatus运行命令(例如suicide)并报告内核退出状态。

这是suicide.c

/*
@(#)File:           $RCSfile: suicide.c,v $
@(#)Version:        $Revision: 1.2 $
@(#)Last changed:   $Date: 2008/12/28 03:45:18 $
@(#)Purpose:        Commit suicide using kill()
@(#)Author:         J Leffler
@(#)Copyright:      (C) JLSS 2008
@(#)Product:        :PRODUCT:
*/

/*TABSTOP=4*/

#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif /* __STDC_VERSION__ */

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "stderr.h"

static const char usestr[] = "[-V][-s signal]";

#ifndef lint
/* Prevent over-aggressive optimizers from eliminating ID string */
extern const char jlss_id_suicide_c[];
const char jlss_id_suicide_c[] = "@(#)$Id: suicide.c,v 1.2 2008/12/28 03:45:18 jleffler Exp $";
#endif /* lint */

int main(int argc, char **argv)
{
    int signum = SIGINT;
    int opt;
    char *end;

    err_setarg0(argv[0]);

    while ((opt = getopt(argc, argv, "Vs:")) != -1)
    {
        switch (opt)
        {
        case 's':
            signum = strtol(optarg, &end, 0);
            if (*end != '\0' || signum <= 0)
                err_error("invalid signal number %s\n", optarg);
            break;
        case 'V':
            err_version("SUICIDE", &"@(#)$Revision: 1.2 $ ($Date: 2008/12/28 03:45:18 $)"[4]);
            break;
        default:
            err_usage(usestr);
            break;
        }
    }
    if (optind != argc)
        err_usage(usestr);
    kill(getpid(), signum);
    return(0);
}

这里是exitstatus.c

/*
@(#)File:           $RCSfile: exitstatus.c,v $
@(#)Version:        $Revision: 1.2 $
@(#)Last changed:   $Date: 2008/12/28 03:45:18 $
@(#)Purpose:        Run command and report 16-bit exit status
@(#)Author:         J Leffler
@(#)Copyright:      (C) JLSS 2008
@(#)Product:        :PRODUCT:
*/

/*TABSTOP=4*/

#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif /* __STDC_VERSION__ */

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "stderr.h"

#ifndef lint
/* Prevent over-aggressive optimizers from eliminating ID string */
extern const char jlss_id_exitstatus_c[];
const char jlss_id_exitstatus_c[] = "@(#)$Id: exitstatus.c,v 1.2 2008/12/28 03:45:18 jleffler Exp $";
#endif /* lint */

int main(int argc, char **argv)
{
    pid_t pid;

    err_setarg0(argv[0]);

    if (argc < 2)
        err_usage("cmd [args...]");

    if ((pid = fork()) < 0)
        err_syserr("fork() failed: ");
    else if (pid == 0)
    {
        /* Child */
        execvp(argv[1], &argv[1]);
        return(1);
    }
    else
    {
        pid_t corpse;
        int status;
        corpse = waitpid(pid, &status, 0);
        if (corpse != pid)
            err_syserr("waitpid() failed: ");
        printf("0x%04X\n", status);
    }
    return(0);
}

缺少的代码stderr.cstderr.h可以很容易地在我发布的任何程序中找到。如果您迫切需要,请从IIUG Software Archive的SQLCMD程序中获取;或者,通过电子邮件与我联系(参见我的个人资料)。