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 elseset -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 elset -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 Elfalse
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 .