GNU/Linux >> Tutoriales Linux >  >> Linux

Generar error en un script Bash

Hay un par de formas más con las que puede abordar este problema. Suponiendo que uno de sus requisitos es ejecutar una secuencia de comandos/función de shell que contenga algunos comandos de shell y verificar si la secuencia de comandos se ejecutó correctamente y arrojar errores en caso de fallas.

Los comandos de shell generalmente se basan en códigos de salida devueltos para que el shell sepa si tuvo éxito o falló debido a algunos eventos inesperados.

Entonces, lo que quieres hacer cae dentro de estas dos categorías

  • salir por error
  • salir y limpiar en caso de error

Dependiendo de cuál quiera hacer, hay opciones de shell disponibles para usar. Para el primer caso, el shell proporciona una opción con set -e y por el segundo podrías hacer un trap el EXIT

¿Debería usar exit? en mi script/función?

Usando exit generalmente mejora la legibilidad En ciertas rutinas, una vez que sabe la respuesta, desea salir a la rutina de llamada inmediatamente. Si la rutina está definida de tal manera que no requiere más limpieza una vez que detecta un error, no salir inmediatamente significa que tiene que escribir más código.

Por lo tanto, en los casos en que necesite realizar acciones de limpieza en la secuencia de comandos para que la finalización de la secuencia de comandos quede limpia, es preferible no usar exit .

¿Debería usar set -e? por error al salir?

¡No!

set -e fue un intento de agregar "detección automática de errores" al shell. Su objetivo era hacer que el shell abortara cada vez que ocurriera un error, pero tiene muchas trampas potenciales, por ejemplo,

  • Los comandos que forman parte de una prueba if son inmunes. En el ejemplo, si espera que se rompa en el test verifique el directorio que no existe, no lo haría, pasa a la condición else

    set -e
    f() { test -d nosuchdir && echo no dir; }
    f
    echo survived
    
  • Los comandos en una canalización que no sea la última son inmunes. En el ejemplo a continuación, debido a que se considera el código de salida del comando ejecutado más recientemente (más a la derecha) ( cat ) y tuvo éxito. Esto podría evitarse configurando el set -o pipefail opción, pero sigue siendo una advertencia.

    set -e
    somecommand that fails | cat -
    echo survived 
    

Uso recomendado - trap al salir

El veredicto es si desea poder manejar un error en lugar de salir a ciegas, en lugar de usar set -e , usa un trap en el ERR pseudoseñal.

El ERR trap no es ejecutar código cuando el propio shell sale con un código de error distinto de cero, sino cuando cualquier comando ejecutado por ese shell que no sea parte de una condición (como en if cmd , o cmd || ) sale con un estado de salida distinto de cero.

La práctica general es que definimos un controlador de trampas para proporcionar información de depuración adicional sobre qué línea y qué causa la salida. Recuerda el código de salida del último comando que provocó el ERR la señal aún estaría disponible en este punto.

cleanup() {
    exitcode=$?
    printf 'error condition hit\n' 1>&2
    printf 'exit code returned: %s\n' "$exitcode"
    printf 'the command executing at the time of the error was: %s\n' "$BASH_COMMAND"
    printf 'command present on line: %d' "${BASH_LINENO[0]}"
    # Some more clean up code can be added here before exiting
    exit $exitcode
}

y solo usamos este controlador como se muestra a continuación en la parte superior del script que está fallando

trap cleanup ERR

Juntando esto en un script simple que contenía false en la línea 15, la información que obtendrías como

error condition hit
exit code returned: 1
the command executing at the time of the error was: false
command present on line: 15

El trap también proporciona opciones independientemente del error para simplemente ejecutar la limpieza al finalizar el shell (por ejemplo, su script de shell sale), en la señal EXIT . También puede atrapar varias señales al mismo tiempo. La lista de señales admitidas para interceptar se puede encontrar en trap.1p - página del manual de Linux

Otra cosa a tener en cuenta sería comprender que ninguno de los métodos proporcionados funciona si se trata de subcapas, en cuyo caso, es posible que deba agregar su propio manejo de errores.

  • En una subcapa con set -e no funcionaría El false está restringida a la subcapa y nunca se propaga a la capa principal. Para hacer el manejo de errores aquí, agregue su propia lógica para hacer (false) || false

    set -e
    (false)
    echo survived
    
  • Lo mismo sucede con trap además. La lógica a continuación no funcionaría por las razones mencionadas anteriormente.

    trap 'echo error' ERR
    (false)
    

Manejo básico de errores

Si su ejecutor de casos de prueba devuelve un código distinto de cero para las pruebas fallidas, simplemente puede escribir:

test_handler test_case_x; test_result=$?
if ((test_result != 0)); then
  printf '%s\n' "Test case x failed" >&2  # write error message to stderr
  exit 1                                  # or exit $test_result
fi

O incluso más corto:

if ! test_handler test_case_x; then
  printf '%s\n' "Test case x failed" >&2
  exit 1
fi

O el más corto:

test_handler test_case_x || { printf '%s\n' "Test case x failed" >&2; exit 1; }

Para salir con el código de salida de test_handler:

test_handler test_case_x || { ec=$?; printf '%s\n' "Test case x failed" >&2; exit $ec; }

Manejo avanzado de errores

Si desea adoptar un enfoque más completo, puede tener un controlador de errores:

exit_if_error() {
  local exit_code=$1
  shift
  [[ $exit_code ]] &&               # do nothing if no error code passed
    ((exit_code != 0)) && {         # do nothing if error code is 0
      printf 'ERROR: %s\n' "[email protected]" >&2 # we can use better logging here
      exit "$exit_code"             # we could also check to make sure
                                    # error code is numeric when passed
    }
}

luego invóquelo después de ejecutar su caso de prueba:

run_test_case test_case_x
exit_if_error $? "Test case x failed"

o

run_test_case test_case_x || exit_if_error $? "Test case x failed"

Las ventajas de tener un controlador de errores como exit_if_error son:

  • podemos estandarizar toda la lógica de manejo de errores, como el registro, la impresión de un seguimiento de la pila, la notificación, la limpieza, etc., en un solo lugar
  • al hacer que el controlador de errores obtenga el código de error como argumento, podemos ahorrarle a la persona que llama el desorden de if bloques que prueban los códigos de salida en busca de errores
  • si tenemos un manejador de señales (usando trap), podemos invocar el manejador de errores desde allí

Biblioteca de manejo y registro de errores

Aquí hay una implementación completa del manejo y registro de errores:

https://github.com/codeforester/base/blob/master/lib/stdlib.sh

Publicaciones relacionadas

  • Manejo de errores en Bash
  • El comando incorporado 'caller' en Bash Hackers Wiki
  • ¿Existen códigos de estado de salida estándar en Linux?
  • BashFAQ/105 - ¿Por qué establecer -e (o establecer -o errexit o trap ERR) no hace lo que esperaba?
  • Equivalente a __FILE__ , __LINE__ en Bash
  • ¿Hay un comando TRY CATCH en Bash
  • Para agregar un seguimiento de pila al controlador de errores, es posible que desee consultar esta publicación:Seguimiento de programas ejecutados llamados por un script Bash
  • Ignorar errores específicos en un script de shell
  • Capturar códigos de error en un shell pipe
  • ¿Cómo administro la verbosidad del registro dentro de un script de shell?
  • ¿Cómo registrar el nombre de la función y el número de línea en Bash?
  • ¿Son preferibles los corchetes dobles [[ ]] a los corchetes simples [ ] en Bash?

Esto depende de dónde desee que se almacene el mensaje de error.

Puede hacer lo siguiente:

echo "Error!" > logfile.log
exit 125

O lo siguiente:

echo "Error!" 1>&2
exit 64

Cuando genera una excepción, detiene la ejecución del programa.

También puedes usar algo como exit xxx donde xxx es el código de error que puede querer devolver al sistema operativo (de 0 a 255). Aquí 125 y 64 son solo códigos aleatorios con los que puede salir. Cuando necesite indicarle al sistema operativo que el programa se detuvo de manera anormal (por ejemplo, ocurrió un error), debe pasar un código de salida distinto de cero a exit .

Como señaló @chepner, puede hacer exit 1 , lo que significará un error no especificado .


Linux
  1. Aprenda el manejo de errores de Bash con el ejemplo

  2. ¿Se puede conectar un script Bash a un archivo?

  3. Typeset -a ¿Está dando un error en el script?

  4. Bash Script para completar una plantilla?

  5. Error de secuencia de comandos Bash:¿Se esperaba una expresión entera?

Comando de salida de Bash y códigos de salida

35 ejemplos de secuencias de comandos Bash

Manejo de errores en scripts Bash

Cómo ejecutar un script Bash

¿Qué significa set -e en un script bash?

Ejecute el comando bash en la canalización de jenkins