Cómo atrapar errores en Bash Scripts en Linux
Publicado: 2022-08-27De manera predeterminada, un script Bash en Linux informará un error pero seguirá ejecutándose. Le mostramos cómo manejar los errores usted mismo para que pueda decidir qué debe suceder a continuación.
Manejo de errores en scripts
El manejo de errores es parte de la programación. Incluso si escribe un código impecable, aún puede encontrarse con condiciones de error. El entorno de su computadora cambia con el tiempo, a medida que instala y desinstala software, crea directorios y realiza mejoras y actualizaciones.
Por ejemplo, una secuencia de comandos que solía ejecutarse sin problemas puede tener dificultades si cambian las rutas del directorio o si se modifican los permisos en un archivo. La acción predeterminada del shell Bash es imprimir un mensaje de error y continuar con la ejecución del script. Este es un valor predeterminado peligroso.
Si la acción que falló es crítica para algún otro procesamiento o acción que ocurra más adelante en su secuencia de comandos, esa acción crítica no tendrá éxito. Lo desastroso que resulte depende de lo que el script intente hacer.
Un esquema más robusto detectaría errores y permitiría que el script funcionara si necesita apagarse o tratar de remediar la condición de falla. Por ejemplo, si falta un directorio o un archivo, puede ser satisfactorio que el script los vuelva a crear.
Si el script ha encontrado un problema del que no puede recuperarse, puede cerrarse. Si el script tiene que cerrarse, puede tener la oportunidad de realizar cualquier limpieza necesaria, como eliminar archivos temporales o escribir la condición de error y el motivo del cierre en un archivo de registro.
Detección del estado de salida
Los comandos y programas generan un valor que se envía al sistema operativo cuando terminan. Esto se llama su estado de salida. Tiene un valor de cero si no hubo errores, o algún valor distinto de cero si ocurrió un error.
Podemos verificar el estado de salida, también conocido como código de retorno, de los comandos que usa el script y determinar si el comando fue exitoso o no.
En Bash, cero equivale a verdadero. Si la respuesta del comando no es verdadera, sabemos que ha ocurrido un problema y podemos tomar las medidas apropiadas.
Copie este script en un editor y guárdelo en un archivo llamado "bad_command.sh".
#!/bin/bash si (! comando_malo); después echo "bad_command marcó un error". salida 1 fi
Deberá hacer que el script sea ejecutable con el comando chmod
. Este es un paso que se requiere para hacer que cualquier secuencia de comandos sea ejecutable, por lo que si desea probar las secuencias de comandos en su propia máquina, recuerde hacer esto para cada una de ellas. Sustituya el nombre del script apropiado en cada caso.
chmod +x mal_comando.sh
Cuando ejecutamos el script, vemos el mensaje de error esperado.
./mal_comando.sh
No existe un comando como "bad_command", ni es el nombre de una función dentro del script. No se puede ejecutar, por lo que la respuesta no es cero. Si la respuesta no es cero (el signo de exclamación se usa aquí como el operador lógico NOT
), se ejecuta el cuerpo de la instrucción if
.
En una secuencia de comandos del mundo real, esto podría terminar la secuencia de comandos, lo que hace nuestro ejemplo, o podría intentar remediar la condición de falla.
Puede parecer que la línea exit 1
es redundante. Después de todo, no hay nada más en el script y terminará de todos modos. Pero usar el comando exit
nos permite pasar un estado de salida al shell. Si alguna vez se llama a nuestro script desde un segundo script, ese segundo script sabrá que este script encontró errores.
Puede usar el operador lógico OR
con el estado de salida de un comando y llamar a otro comando o una función en su secuencia de comandos si hay una respuesta distinta de cero del primer comando.
comando_1 || comando_2
Esto funciona porque se ejecuta el primer comando OR
el segundo. El comando más a la izquierda se ejecuta primero. Si tiene éxito, el segundo comando no se ejecuta. Pero si falla el primer comando, se ejecuta el segundo comando. Entonces podemos estructurar un código como este. Esto es "lógico-o./sh".
#!/bin/bash manejador_de_errores() { echo "Error: ($?) $1" salida 1 } mal_comando || error_handler "bad_command falló, Línea: ${LINENO}"
Hemos definido una función llamada error_handler
. Esto imprime el estado de salida del comando fallido, contenido en la variable $?
y una línea de texto que se le pasa cuando se llama a la función. Esto se lleva a cabo en la variable $1
. La función finaliza el script con un estado de salida de uno.
El script intenta ejecutar bad_command
que obviamente falla, por lo que el comando a la derecha del operador lógico OR
, ||
, es ejecutado. Esto llama a la función error_handler
y pasa una cadena que nombra el comando que falló y contiene el número de línea del comando fallido.
Ejecutaremos la secuencia de comandos para ver el mensaje del controlador de errores y luego verificaremos el estado de salida de la secuencia de comandos usando echo.
./lógico-o.sh
eco $?
Nuestra pequeña función error_handler
proporciona el estado de salida del intento de ejecutar bad_command
, el nombre del comando y el número de línea. Esta es información útil cuando está depurando un script.
El estado de salida del script es uno. El estado de salida 127 informado por error_handler
significa "comando no encontrado". Si quisiéramos, podríamos usar eso como el estado de salida del script pasándolo al comando de exit
.
Otro enfoque sería expandir error_handler
para verificar los diferentes valores posibles del estado de salida y realizar diferentes acciones en consecuencia, utilizando este tipo de construcción:
exit_code=$? if [ $código_salida -eq 1 ]; después echo "Operación no permitida" elif [ $código_salida -eq 2 ]; después echo "Mal uso de shell incorporados" . . . elif [ $estado -eq 128 ]; después echo "Argumento inválido" fi
Usar set para forzar una salida
Si sabe que quiere que su secuencia de comandos se cierre siempre que haya un error, puede forzarlo a que lo haga. significa que renuncia a la posibilidad de cualquier limpieza, o también de cualquier daño adicional, porque su secuencia de comandos finaliza tan pronto como detecta un error.
Para hacer esto, use el comando set
con la opción -e
(error). Esto le dice a la secuencia de comandos que salga cada vez que un comando falla o devuelve un código de salida mayor que cero. Además, el uso de la opción -E
garantiza que la detección de errores y la captura funcionen en las funciones de shell.
Para capturar también variables no inicializadas, agregue la opción -u
(sin configurar). Para asegurarse de que se detecten errores en las secuencias canalizadas, agregue la opción -o pipefail
. Sin esto, el estado de salida de una secuencia canalizada de comandos es el estado de salida del comando final en la secuencia. No se detectaría un comando fallido en medio de la secuencia canalizada. La opción -o pipefail
debe estar en la lista de opciones.
La secuencia para agregar a la parte superior de su secuencia de comandos es:
conjunto -Eeuo pipefail
Aquí hay un script corto llamado "unset-var.sh", con una variable unset en él.
#!/bin/bash conjunto -Eeou pipefail echo "$unset_variable" echo "¿Vemos esta línea?"
Cuando ejecutamos el script, unset_variable se reconoce como una variable no inicializada y el script finaliza.
./unset-var.sh
El segundo comando echo
nunca se ejecuta.
Usar trap con errores
El comando Bash trap le permite designar un comando o una función que debe llamarse cuando se genera una señal en particular. Por lo general, esto se usa para captar señales como SIGINT
, que se genera cuando presiona la combinación de teclas Ctrl+C. Este script es "sigint.sh".
#!/bin/bash trap "echo -e '\nTerminado con Ctrl+c'; salir" SIGINT contador=0 mientras que es cierto hacer echo "Número de bucle:" $((++contador)) dormir 1 hecho
El comando trap
contiene un comando echo
y el comando exit
. Se activará cuando se SIGINT
. El resto del script es un bucle simple. Si ejecuta la secuencia de comandos y pulsa Ctrl+C, verá el mensaje de la definición de trap
y la secuencia de comandos finalizará.
./sigint.sh
Podemos usar trap
con la señal ERR
para detectar errores a medida que ocurren. Estos pueden luego ser alimentados a un comando o función. Esto es "trap.sh". Estamos enviando notificaciones de error a una función llamada error_handler
.
#!/bin/bash atrapar 'controlador_de_errores $? $LINENO' ERR manejador_de_errores() { echo "Error: ($1) ocurrió en $2" } principal() { echo "Dentro de la función principal ()" mal_comando segundo tercera salir $? } segundo() { echo "Después de llamar a main()" echo "Dentro de la función second()" } tercera() { echo "Dentro de la tercera función ()" } principal
La mayor parte del script está dentro de la función main
, que llama a las funciones second
y third
. Cuando se encuentra un error, en este caso, porque bad_command
no existe, la instrucción trap
dirige el error a la función error_handler
. Pasa el estado de salida del comando fallido y el número de línea a la función error_handler
.
./trampa.sh
Nuestra función error_handler
simplemente enumera los detalles del error en la ventana del terminal. Si lo desea, puede agregar un comando de exit
a la función para que finalice el script. O podría usar una serie de declaraciones if/elif/fi
para realizar diferentes acciones para diferentes errores.
Podría ser posible remediar algunos errores, otros podrían requerir que la secuencia de comandos se detuviera.
Un consejo final
Detectar errores a menudo significa adelantarse a las cosas que pueden salir mal y poner un código para manejar esas eventualidades en caso de que surjan. Eso es además de asegurarse de que el flujo de ejecución y la lógica interna de su secuencia de comandos sean correctos.
Si usa este comando para ejecutar su secuencia de comandos, Bash le mostrará una salida de seguimiento a medida que se ejecuta la secuencia de comandos:
bash -x tu-script.sh
Bash escribe el resultado de la traza en la ventana del terminal. Muestra cada comando con sus argumentos, si los tiene. Esto sucede después de que se hayan expandido los comandos, pero antes de que se ejecuten.
Puede ser de gran ayuda para rastrear errores esquivos.
RELACIONADO: Cómo validar la sintaxis de un script Bash de Linux antes de ejecutarlo