unexpected then script expecting error end linux bash shell error-handling

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 bloques if 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 else

    set -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ón set -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. Lo false 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

  1. 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.

  1. 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.