linux - then - Elevar error en un script Bash
syntax error unexpected php (6)
Manejo básico de errores
Si su corredor 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 integral, 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'' "$@" >&2 # we can use better logging here
exit "$exit_code" # we could also check to make sure
# error code is numeric when passed
}
}
luego invocarlo 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 logging , 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 evitar que la persona que llama
if
llena de bloquesif
que prueban los códigos de salida en busca de errores - si tenemos un controlador de señal (usando trap ), podemos invocar el controlador de error desde allí
Biblioteca de registro y manejo de errores
Aquí hay una implementación completa de manejo y registro de errores:
https://github.com/codeforester/base/blob/master/lib/stdlib.sh
Artículos Relacionados
- Manejo de errores en Bash
- El comando incorporado ''llamador'' en Bash Hackers Wiki
- ¿Hay algún código de estado de salida estándar en Linux?
- BashFAQ / 105 - ¿Por qué set -e (o set -o errexit, o trap ERR) no hacen lo que esperaba?
-
Equivalente de
__FILE__
,__LINE__
en Bash - ¿Hay un comando TRY CATCH en Bash
- Para agregar un seguimiento de la pila al controlador de errores, puede consultar esta publicación: Seguimiento de programas ejecutados llamados por un script Bash
- Ignorar errores específicos en un script de shell
- Captura de códigos de error en una tubería de shell
- logging
- ¿Cómo registrar el nombre de la función y el número de línea en Bash?
- ¿Es preferible el uso de corchetes dobles [[]] sobre corchetes simples [] en Bash?
Quiero generar un error en un script Bash con el mensaje "¡Falló el caso de prueba!". ¿Cómo hacer esto en Bash?
Por ejemplo:
if [ condition ]; then
raise error "Test cases failed !!!"
fi
A menudo me resulta útil escribir una función para manejar mensajes de error para que el código sea más limpio en general.
# Usage: die [exit_code] [error message]
die() {
local code=$? now=$(date +%T.%N)
if [ "$1" -ge 0 ] 2>/dev/null; then # assume $1 is an error code if numeric
code="$1"
shift
fi
echo "$0: ERROR at ${now%???}${1:+: $*}" >&2
exit $code
}
Esto toma el código de error del comando anterior y lo usa como el código de error predeterminado al salir de todo el script.
También señala el tiempo, con microsegundos donde se admite (la fecha de GNU
%N
es nanosegundos, que truncamos a microsegundos más adelante).
Si la primera opción es cero o un entero positivo, se convierte en el código de salida y lo eliminamos de la lista de opciones.
Luego informamos el mensaje al error estándar, con el nombre del script, la palabra "ERROR" y la hora (usamos la expansión de parámetros para truncar nanosegundos a microsegundos, o para tiempos no GNU, para truncar, por ejemplo,
12:34:56.%N
a
12:34:56
).
Se agregan dos puntos y un espacio después de la palabra ERROR, pero solo cuando hay un mensaje de error proporcionado.
Finalmente, salimos de la secuencia de comandos usando el código de salida determinado previamente, activando cualquier trampa de forma normal.
Algunos ejemplos (suponga que el código vive en
script.sh
):
if [ condition ]; then die 123 "condition not met"; fi
# exit code 123, message "script.sh: ERROR at 14:58:01.234564: condition not met"
$command |grep -q condition || die 1 "''$command'' lacked ''condition''"
# exit code 1, "script.sh: ERROR at 14:58:55.825626: ''foo'' lacked ''condition''"
$command || die
# exit code comes from command''s, message "script.sh: ERROR at 14:59:15.575089"
Aquí hay una trampa simple que imprime el último argumento de lo que falló en STDERR, informa la línea en la que falló y sale del script con el número de línea como código de salida. Tenga en cuenta que estas no siempre son buenas ideas, pero esto demuestra algunas aplicaciones creativas en las que podría construir.
trap ''echo >&2 "$_ at $LINENO"; exit $LINENO;'' ERR
Lo puse en un script con un bucle para probarlo. Solo compruebo un hit en algunos números aleatorios; Puedes usar pruebas reales. Si necesito pagar la fianza, llamo falso (que activa la trampa) con el mensaje que quiero lanzar.
Para una funcionalidad elaborada, haga que la trampa llame a una función de procesamiento. Siempre puede usar una declaración de caso en su arg ($ _) si necesita hacer más limpieza, etc. Asignar a una var para un poco de azúcar sintáctica:
trap ''echo >&2 "$_ at $LINENO"; exit $LINENO;'' ERR
throw=false
raise=false
while :
do x=$(( $RANDOM % 10 ))
case $x in
0) $throw "DIVISION BY ZERO" ;;
3) $raise "MAGIC NUMBER" ;;
*) echo got $x ;;
esac
done
Salida de muestra:
# bash tst
got 2
got 8
DIVISION BY ZERO at 6
# echo $?
6
Obviamente, podrías
runTest1 "Test1 fails" # message not used if it succeeds
Mucho espacio para la mejora del diseño.
Los inconvenientes incluyen el hecho de que
false
no es bonito (por lo tanto, el azúcar), y otras cosas que hacen que la trampa se vea un poco estúpida.
Aún así, me gusta este método.
Esto depende de dónde desea que se almacene el mensaje de error.
Puedes 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 puede usar algo como
exit xxx
donde
xxx
es el código de error que desea devolver al sistema operativo (de 0 a 255).
Aquí
125
y
64
son solo códigos aleatorios con los que puede salir.
Cuando necesite indicar al sistema operativo que el programa se detuvo de manera anormal (por ejemplo, ocurrió un error), debe pasar un
código
de
exit
distinto de cero
para
exit
.
Como
señaló
@chepner, puede hacer la
exit 1
, lo que significará
un error no especificado
.
Hay un par de formas más para abordar este problema. Asumiendo que uno de sus requisitos es ejecutar un script / función de shell que contenga algunos comandos de shell y verificar si el script 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 recae en estas dos categorías
- salir por error
- salida y limpieza por error
Dependiendo de cuál desee hacer, hay opciones de shell disponibles para usar.
Para el primer caso, el shell proporciona una opción con
set -e
y para el segundo podría hacer una
trap
al
EXIT
¿Debo usar
exit
en mi script / función?
El uso de la
exit
generalmente mejora la legibilidad En ciertas rutinas, una vez que sabe la respuesta, desea salir de la rutina de llamada de inmediato.
Si la rutina se define de tal manera que no requiere más limpieza una vez que detecta un error, no salir inmediatamente significa que debe escribir más código.
Entonces, en caso de que necesite realizar acciones de limpieza en el script para limpiar la terminación del script, es preferible
no
usar
exit
.
¿Debo usar
set -e
para 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 viene con 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 la comprobación de
test
en el directorio no existente, no lo haría, pasará a la condición elseset -e f() { test -d nosuchdir && echo no dir; } f echo survived
-
Los comandos en una tubería diferente a la última, son inmunes. En el ejemplo a continuación, porque el código de salida del comando ejecutado más recientemente (más a la derecha) se considera (
cat
) y fue exitoso. Esto podría evitarse mediante la configuración de la opciónset -o pipefail
pero sigue siendo una advertencia.set -e somecommand that fails | cat - echo survived
Recomendado para usar:
trap
en la salida
El veredicto es si desea poder manejar un error en lugar de salir a ciegas, en lugar de usar
set -e
, use una
trap
en la pseudo señal
ERR
.
La trampa
ERR
no es ejecutar código cuando el shell en sí mismo sale con un código de error distinto de cero, sino cuando cualquier comando ejecutado por ese shell que no es 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 trampa para proporcionar información de depuración adicional en qué línea y qué causa la salida.
Recuerde que el código de salida del último comando que causó la señal
ERR
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 falla
trap cleanup ERR
Al reunir esto en un script simple que contenía
false
en la línea 15, la información que obtendría como
error condition hit
exit code returned: 1
the command executing at the time of the error was: false
command present on line: 15
La
trap
también proporciona opciones, independientemente del error, para ejecutar la limpieza al finalizar el shell (por ejemplo, el script de shell se cierra), en la señal
EXIT
.
También podría atrapar múltiples señales al mismo tiempo.
La lista de señales admitidas para atrapar 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 está trabajando con subcapas, en cuyo caso, es posible que deba agregar su propio manejo de errores.
-
En un sub-shell con
set -e
no funcionaría. Lofalse
está restringido a la sub-shell y nunca se propaga a la shell principal. Para hacer el manejo de errores aquí, agregue su propia lógica para hacer(false) || false
(false) || false
set -e (false) echo survived
-
Lo mismo sucede con la
trap
también. La lógica a continuación no funcionaría por las razones mencionadas anteriormente.trap ''echo error'' ERR (false)
Tiene 2 opciones: redirigir la salida del script a un archivo, introducir un archivo de registro en el script y
- Redirigiendo la salida a un archivo :
Aquí asume que el script genera toda la información necesaria, incluidos los mensajes de advertencia y error. Luego puede redirigir la salida a un archivo de su elección.
./runTests &> output.log
El comando anterior redirige tanto la salida estándar como la salida de error a su archivo de registro.
Con este enfoque, no tiene que introducir un archivo de registro en el script, por lo que la lógica es un poco más fácil.
- Introduzca un archivo de registro en el script :
En su secuencia de comandos, agregue un archivo de registro codificándolo:
logFile=''./path/to/log/file.log''
o pasándolo por un parámetro:
logFile="${1}" # This assumes the first parameter to the script is the log file
Es una buena idea agregar la marca de tiempo en el momento de la ejecución al archivo de registro en la parte superior del script:
date ''+%Y%-m%d-%H%M%S'' >> "${logFile}"
Luego puede redirigir sus mensajes de error al archivo de registro
if [ condition ]; then
echo "Test cases failed!!" >> "${logFile}";
fi
Esto agregará el error al archivo de registro y continuará la ejecución.
Si desea detener la ejecución cuando se producen errores críticos, puede
exit
del script:
if [ condition ]; then
echo "Test cases failed!!" >> "${logFile}";
# Clean up if needed
exit 1;
fi
Tenga en cuenta que la
exit 1
indica que el programa detiene la ejecución debido a un error no especificado.
Puede personalizar esto si lo desea.
Con este enfoque, puede personalizar sus registros y tener un archivo de registro diferente para cada componente de su secuencia de comandos.
Si tiene un script relativamente pequeño o desea ejecutar el script de otra persona sin modificarlo, el primer enfoque es más adecuado.
Si siempre desea que el archivo de registro esté en la misma ubicación, esta es la mejor opción del 2. Además, si ha creado un gran script con múltiples componentes, entonces puede que desee registrar cada parte de manera diferente y el segundo enfoque es su único opción.