Todo sobre Apple, Android, Juegos Apks y Sitios de Peliculas

Cómo detectar errores en scripts Bash en Linux

De forma 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

Manejar errores es parte de la programación. Incluso si escribe 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, un script que solía ejecutarse sin problemas puede tener dificultades si las rutas del directorio cambian o si se cambian los permisos en un archivo. La acción predeterminada del shell Bash es imprimir un mensaje de error y continuar ejecutando el script. Este es un incumplimiento peligroso.

Si la acción que falló es crítica para algún otro procesamiento o acción que sucede más adelante en su script, esa acción crítica no tendrá éxito. Lo desastroso que resulte depende de lo que intente hacer su guión.

Un esquema más sólido detectaría errores y permitiría que el script funcionara si fuera necesario cerrarlo o intentar remediar la condición de falla. Por ejemplo, si falta un directorio o archivo, puede ser satisfactorio que el script los vuelva a crear.

Si el script ha encontrado un problema del cual 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.

Detectar el estado de salida

Los comandos y programas generan un valor que se envía al sistema operativo cuando finalizan. 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 adecuadas.

Copie este script en un editor y guárdelo en un archivo llamado «bad_command.sh».

#!/bin/bash

if ( ! bad_command ); then

  echo "bad_command flagged an error."

  exit 1

fi

Deberá hacer que el script sea ejecutable con el comando chmod. Este es un paso necesario para que cualquier script sea ejecutable, por lo que si desea probar los scripts en su propia máquina, recuerde hacer esto para cada uno de ellos. Sustituya el nombre del script apropiado en cada caso.

chmod +x bad_command.sh

Cuando ejecutamos el script vemos el mensaje de error esperado.

./bad_command.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, aquí se utiliza el signo de exclamación como operador NOT lógico, se ejecuta el cuerpo de la instrucción if.

En un script del mundo real, esto podría finalizar el script, como ocurre en nuestro ejemplo, o podría intentar remediar la condición de falla.

Podría parecer la salida. 1 La línea es redundante. Después de todo, no hay nada más en el guión 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 utilizar el operador lógico OR con el estado de salida de un comando y llamar a otro comando o función en su secuencia de comandos si hay una respuesta distinta de cero del primer comando.

command_1 || command_2

Esto funciona porque se ejecuta el primer comando O el segundo. El comando más a la izquierda se ejecuta primero. Si tiene éxito, el segundo comando no se ejecuta. Pero si el primer comando falla, se ejecuta el segundo comando. Entonces podemos estructurar código como este. Esto es «lógico-o./sh».

#!/bin/bash

error_handler()

{

  echo "Error: ($?) $1"

  exit 1

}

bad_command || error_handler "bad_command failed, Line: ${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 mantiene en la variable $1. La función finaliza el script con un estado de salida de uno.

El script intenta ejecutar bad_command, lo que obviamente falla, por lo que se ejecuta el comando a la derecha del operador lógico OR, ||. 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 el script para ver el mensaje del controlador de errores y luego verificaremos el estado de salida del script usando echo.

./logical-or.sh
echo $?

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ás 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 usarlo como estado de salida del script pasándolo al comando de salida.

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 [ $exit_code -eq 1 ]; then

echo "Operation not permitted"

elif [ $exit_code -eq 2 ]; then

echo "Misuse of shell builtins"

.

.

.

elif [ $status -eq 128 ]; then

echo "Invalid argument"

fi

Usando set para forzar una salida

Si sabe que desea que su script se cierre cada vez que haya un error, puede forzarlo a que lo haga. significa que usted renuncia a la posibilidad de realizar 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 indica al script 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 y captura de errores funcione en las funciones del shell.

Relacionado: Solución: Error «Mal intérprete: no existe tal archivo o directorio» en Linux

Para capturar también variables no inicializadas, agregue la opción -u (desarmar). 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 de la secuencia. No se detectaría un comando fallido en medio de la secuencia canalizada. La opción -o pipefail debe aparecer en la lista de opciones.

La secuencia para agregar al principio de su script es:

set -Eeuo pipefail

Aquí hay un breve script llamado «unset-var.sh», con una variable no configurada.

#!/bin/bash

set -Eeou pipefail

echo "$unset_variable"

echo "Do we see this line?"

Cuando ejecutamos el script, unset_variable se reconoce como una variable no inicializada y el script finaliza.

./unset-var.sh

El segundo comando de eco nunca se ejecuta.

Usando trampa 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. Normalmente, esto se utiliza para captar señales como SIGINT, que aparece cuando presiona la combinación de teclas Ctrl+C. Este script es «sigint.sh».

#!/bin/bash

trap "echo -e '\nTerminated by Ctrl+c'; exit" SIGINT

counter=0

while true

do

echo "Loop number:" $((++counter))

sleep 1

done

El comando trap contiene un comando echo y el comando exit. Se activará cuando se eleve SIGINT. El resto del guión es un bucle simple. Si ejecuta el script y presiona Ctrl+C, verá el mensaje de la definición de trampa y el script finalizará.

./sigint.sh

Podemos usar trampa con la señal ERR para detectar errores a medida que ocurren. Luego, estos se pueden enviar a un comando o función. Esto es «trampa.sh». Estamos enviando notificaciones de error a una función llamada error_handler.

#!/bin/bash

trap 'error_handler $? $LINENO' ERR

error_handler() {

echo "Error: ($1) occurred on $2"

}

main() {

echo "Inside main() function"

bad_command

second

third

exit $?

}

second() {

echo "After call to main()"

echo "Inside second() function"

}

third() {

echo "Inside third() function"

}

main

La mayor parte del script está dentro de la función principal, que llama a la segunda y tercera función. 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.

./trap.sh

Nuestra función error_handler simplemente enumera los detalles del error en la ventana de terminal. Si lo desea, puede agregar un comando de salida a la función para finalizar el script. O podría utilizar una serie de declaraciones if/elif/fi para realizar diferentes acciones para diferentes errores.

Es posible que sea posible remediar algunos errores, otros pueden requerir que el script se detenga.

Un último consejo

Detectar errores a menudo significa anticiparse a las cosas que pueden salir mal y escribir 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 script sean correctos.

Si usa este comando para ejecutar su script, Bash le mostrará un resultado de seguimiento a medida que se ejecuta el script:

bash -x your-script.sh

Bash escribe la salida del seguimiento en la ventana de la terminal. Muestra cada comando con sus argumentos, si los tiene. Esto sucede después de que los comandos se hayan expandido pero antes de que se ejecuten.

Puede ser de gran ayuda para localizar errores esquivos.

Relacionado: Cómo validar la sintaxis de un script Bash de Linux antes de ejecutarlo

Resumen del Contenido