Cómo usar trampas y opciones de manejo de errores en Bash 1

Cómo usar trampas y opciones de manejo de errores en Bash

Índice
  1. Ejecutar una función al salir
  2. Hacer que las funciones, subshells y sustituciones de procesos activen traps

Aprende a manejar errores en Bash con esta guía que destaca el uso de `set -e` y `trap` para crear scripts más robustos y limpios.

¿Estás aprendiendo Bash o deseas mejorar el manejo de errores en tus scripts? Las reglas que rigen los errores pueden ser un poco complicadas de entender, así que he preparado esta guía breve para desenterrar su misterio.

El patrón inicial que aprendí—y a menudo el primero que se recomienda—es la opción set -e. Simplemente hace que tu script se cierre cuando detecta un código de salida distinto de cero. Para aquellos que no lo saben, un código de salida es un número devuelto por todos los programas en Linux, y cuando es distinto de cero, significa que ha habido un error. Puedes ver el código de salida del proceso más reciente que ha terminado con echo $?.

Veamos algunos ejemplos.

Cuando todo está bien:

#!/usr/bin/env bash ls / echo Código de salida: $?

Aquí hay un intento de listar un directorio que no existe (un error):

#!/usr/bin/env bash ls /foo echo Código de salida: $?

¿Ves cómo devuelve un código de error distinto de cero?

Ahora usaremos set -e:

#!/usr/bin/env bash set -e ls /foo echo "Sale antes de este punto."

Podemos ver que el script nunca llega a la declaración echo y solo muestra el mensaje de error del "ls".

La opción "set -e" es útil para detener la ejecución cuando un paso falla (similar a una afirmación de postcondición), por ejemplo, mover un archivo que no se descargó correctamente o cambiar los permisos de un archivo en un directorio que no existe.

A menudo también conduce a scripts más limpios, porque no necesitas escribir condiciones para verificar el estado del programa:

 if curl ...; then if mv ...; then if ! chmod ...; then echo "No se pudieron cambiar los permisos del archivo." exit 1 fi else echo "No se pudo mover el archivo." exit 2 fi else echo "No se pudo descargar foo." exit 3 fi

En su lugar, puedes escribir:

 set -e curl ... mv ... chmod ...

Esto es similar al patrón Never Nester, donde el objetivo es aplanar las ramas condicionales. O podrías verlo como una forma implícita de afirmaciones, que obliga a un estado del programa aceptable antes de continuar con la ejecución. De cualquier manera, es menos código, lo cual es más legible, y eso siempre es una gran victoria.

Ejecutar una función al salir

Salir al encontrar errores es una cosa, pero ¿qué hay de limpiar una vez que has terminado? Por ejemplo, has descargado un archivo, lo has extraído, y has intentado mover un solo archivo de él, pero el script luego sale debido a un error. Después de que la ejecución termina, estos archivos no utilizados permanecen en tu sistema de archivos, llenándolo de desorden.

Para mantener nuestro script limpio, queremos evitar ramas de manejo de errores ruidosas, y aquí es donde entran los traps. Un trap ejecuta una función (o comando) cuando ocurre un evento (llamado una señal):

 set -e cd /tmp function cleanup() { rm -rf foo.zip rm -rf foo/ } trap cleanup ERR curl ... --output foo.zip unzip foo.zip mv foo/bar.csv /root/ 

Las señales incluyen ERR, EXIT, INT (interrupción por teclado), y otras. Usa la señal "ERR" para atrapar solo errores, o "EXIT" para ejecutar cada vez que el script se detenga (incluyendo un error).

Estas señales representan las más comunes, pero puedes ver una lista completa con trap -l en Bash. Ten en cuenta que otros shells pueden usar convenciones de nombres diferentes—incluyendo o excluyendo un prefijo "SIG" (por ejemplo, SIGINT). Bash es flexible porque puedes usar ambos.

Si ejecutas un comando (con espacios), envuélvelo entre comillas: trap "rm -rf foo.zip" ERR. También puedes especificar múltiples señales: trap foo ERR INT.

Hacer que las funciones, subshells y sustituciones de procesos activen traps

Si utilizas la opción "set -e", funciones, subshells, y sustituciones de comando no activarán los traps ERR:

#!/usr/bin/env bash set -e trap "echo Trap ejecutado." ERR foo() { ls /bar } foo echo "Sale antes de esto."

Para hacer que las trampas "ERR" funcionen dentro de estos constructos, utiliza la opción "set -E":

#!/usr/bin/env bash set -E trap "echo Trap executed." ERR foo() { ls /bar } foo echo "El script no sale, así que veremos este mensaje."

¿Notaste que la trampa se ejecuta dos veces? Es porque el script no sale al encontrar un error (set -e). Cuando la función devuelve un código de salida diferente de cero ("ls /bar"), el nivel superior técnicamente encuentra un error, y así también ejecuta la trampa. Podemos remediar eso usando set -eE:

#!/usr/bin/env bash set -eE trap "echo Trap executed." ERR foo() { ls /bar } foo echo "Sale antes de este punto."

Un posible problema al usar trampas es gestionar las complejidades de los subshells. Mira este ejemplo:

set -e trap "echo Trap executed." ERR (ls /foo)

Puedes ver que ejecuta la trampa, lo cual no debería suceder porque no hemos usado set -E. ¿Qué está ocurriendo aquí? No pude encontrar nada concreto, pero de lo que puedo deducir:

  1. Los subshells son procesos hijo, que son bifurcaciones (clones) del proceso que llama (incluyendo la opción "set -e"), así que ahora, el subshell también saldrá al encontrar un error.
  2. Los subshells (y otros) no heredan trampas por defecto, así que sabemos que no se ejecuta por el subshell.
  3. "ls /foo" devuelve un valor diferente de cero y hace que el subshell salga con ese código.

Dado los tres puntos, parece que el subshell sale con un valor diferente de cero, y porque el padre encuentra ese error, activa la trampa.

Prueba adicional:

set -e trap "echo Trap executed." ERR ( set +e ls /foo exit 0 )

Las reglas que rigen el comportamiento de las trampas son complejas, así que he creado una hoja de trucos basada en mis observaciones. Los experimentos utilizaron solo set -e (sin set -E):

Nivel superior set -eLa función encuentra un errorset +e en la funciónLa trampa se activa

Las primeras tres columnas definen el contexto, y la cuarta registra el resultado.

Toma la primera fila, por ejemplo: set -e no se utiliza, una función encuentra un error, y ha deshabilitado set -e dentro del alcance de la función.

Los datos sugieren que al habilitar set -e en el nivel superior, la función hereda su valor y, en consecuencia, sale inmediatamente, pero como no hereda trampas (set -E), una trampa no se llama.

Cuando usas set -E, las funciones (y otros constructos) activan sus propias trampas heredadas, y las reglas se vuelven más sencillas.

En la mayoría de los casos al configurar una trampa, usa set -E, porque simplifica enormemente las reglas. Solo ten en cuenta que los subshells y las sustituciones de procesos son procesos bifurcados, que vienen con sus propias peculiaridades ambientales.

Además, siempre uso la opción "set -e" porque continuar un script después de encontrar un error generalmente no es deseable. Para casos extremos, puedes ignorar errores con "|| true":

set -e ls /foo || true echo 'Esto se imprime porque "|| true" ignora el error.'

En resumen, usar set -eE es el enfoque más directo.

Si deseas encontrar artículos similares a Cómo usar trampas y opciones de manejo de errores en Bash, te sugerimos revisar la categoría Linux.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir