2008-12-26 7 views
5

Tengo varios ejecutables independientes de Perl, scripts PHP CLI y programas C++ para los cuales necesito desarrollar una estrategia de código de error de salida. Estos programas son llamados por otros programas utilizando una clase contenedora que he creado para usar exec() en PHP. Entonces, podré recuperar un código de error. En función de ese código de error, el script de llamada deberá hacer algo.¿Qué es una buena estrategia de código de error de salida de Linux?

He hecho un poco de investigación y parece que cualquier cosa en el rango 1-254 (o quizás solo 1-127) podría ser un juego justo para los códigos de error definidos por el usuario.

Me preguntaba cómo otras personas han abordado el manejo de errores en esta situación.

Respuesta

8

La única convención es que devuelve 0 para tener éxito y algo distinto de cero para un error. La mayoría de los programas Unix conocidos documentan los diversos códigos de retorno que pueden devolver, y usted también. No tiene mucho sentido intentar hacer una lista común para todos los códigos de error posibles que cualquier programa arbitrario podría devolver, o bien terminas con tens of thousands of them like some other OS's, e incluso entonces, no siempre cubre el tipo específico de error que quieres devolver

Así que sea constante y asegúrese de documentar cualquier esquema que decida usar.

6

1-127 es el rango disponible. Cualquier cosa por encima de 127 se supone que es una salida "anormal", terminada por una señal.

Mientras lo hace, considere usar stdout en lugar de código de salida. El código de salida se usa tradicionalmente para indicar éxito, falla y puede ser otro estado. En lugar de usar el código de salida, intente usar stdout de la misma forma que expr y wc. A continuación, puede utilizar el marcador invertido o algo similar en la persona que llama para extraer el resultado.

+2

Use stderr para los mensajes de error. – iny

+1

Para ser claros, siempre es posible que un proceso principal indique si se trataba de una señal o una salida normal ... es el _shell_ el que asigna las señales a 129-254 para '$?'. – Random832

3

los estados manifiesto UNIX -

salida tan pronto y tan alto como sea posible en caso de error

o algo por el estilo

2

No trate de codificar demasiado mucho significado en el valor de salida: los estados detallados y los informes de errores deberían ir a stdout/stderr, como sugiere Arkadiy.

Sin embargo, me ha resultado muy útil representar solo un puñado de estados en los valores de salida, usando dígitos binarios para codificarlos. Por ejemplo, suponga que tiene los siguientes significados artificiales:

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

A continuación, un error de entrada del usuario tendría un valor de retorno de 5 (4 + 1), mientras que un archivo de registro no tener permiso de escritura podría tener un valor de retorno de 11 (8 + 2 + 1). Como los diferentes significados están codificados de forma independiente en el valor de retorno, puede ver fácilmente lo que sucede al verificar qué bits están configurados.

Como caso especial, para ver si había un error que puede y con el código de retorno 1.

De esta manera, se puede codificar un par de cosas diferentes en el código de retorno, de forma clara y manera simple. Utilizo esto solo para tomar decisiones simples como "debería reiniciarse el proceso", "devolver el valor de retorno y los registros relevantes a un administrador", ese tipo de cosas. Cualquier información de diagnóstico detallada debe ir a registros o a stdout/stderr.

1

Los estados de salida normales se ejecutan desde 0 hasta 255 (consulte Exit codes bigger than 255 posssible para obtener una explicación de por qué). Normalmente, el estado 0 indica éxito; cualquier otra cosa es un error definido por la implementación. Sí sé de un programa que informa el estado de un servidor DBMS a través del estado de salida; ese es un caso especial de estados de salida definidos por la implementación. Tenga en cuenta que puede definir la implementación de los estados de sus programas.

No pude incluir esto en 300 caracteres; de lo contrario, habría sido un comentario a la respuesta de @ Arkadiy.

Arkadiy tiene razón en que en una parte de la palabra de estado de salida, valores distintos de cero indican la señal que finalizó el proceso y el 8º bit normalmente indica un volcado de núcleo, pero esa sección del estado de salida es diferente de la principal 0..255 estado. Sin embargo, el shell (cualquiera que sea el shell) se presenta con un problema cuando un proceso muere como resultado de una señal. Hay 16 bits de datos que se presentarán en un valor de 8 bits, que siempre es complicado. Lo que las cáscaras parecen hacer es tomar el número de señal y agregarle 128. Entonces, si un proceso muere como resultado de una interrupción (señal número 2, SIGINT), el shell informa que el estado de salida es 130. Sin embargo, el núcleo informó el estado como 0x0002; el shell ha modificado lo que informa el kernel.

El siguiente código C lo demuestra. Hay dos programas

  • suicide que se mata con la señal que elija (interrupción por defecto).
  • exitstatus que ejecuta un comando (como suicide) e informa el estado de salida del kernel.

Aquí es 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); 
} 

Y aquí es 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); 
} 

El código que falta, stderr.c y stderr.h, se pueden encontrar fácilmente en prácticamente cualquier de mis programas publicados. Si lo necesita con urgencia, obténgalo del programa SQLCMD en el IIUG Software Archive; alternativamente, contácteme por correo electrónico (vea mi perfil).

Cuestiones relacionadas