Categories: Terminal

Haz tus scripts de bash más robustos con traps

Imagina que tienes un script de bash que se ejecute cada cierto tiempo y que en algún momento falle o que ese mismo script se ejecute dos veces simultáneamente. Estas dos situaciones son bastante incómodas ya que requieren de la intervención humana para ser corregidas, o en ciertos momentos no pueden ser atendidas dejando la tarea en un estado inconsistente.

trap es una manera sencilla y efectiva de controlar la salida de scripts de bash. Volvamos a la misma situación inicial, si el script es detenido manualmente, por ejemplo con ctrl-c, se interrumpe devolviendo la señal de salida INT y si se termina con kill entonces la salida sería TERM. Todos los códigos de salida posibles se pueden ver con kill -l, sin embargo los más utilizados son precisamente INT, TERM y EXIT. Si el script consiste, por ejemplo, en la sincronización de archivos con rsync lo más sensato es apoyarse en un archivo lock que no permita que el script se ejecute simultáneamente:

LOCK="/var/run/rsync.lock"

if [ ! -e $LOCK ]; then
  touch $LOCK
  rsync -av foo bar
  rm $LOCK
else
   echo "rsync ya se está ejecutando"
fi

En español plano, el script anterior comprueba si existe el archivo /var/run/rsync.lock y si este no existe, lo crea y posteriormente ejecuta el comando correspondiente; por último elimina el archivo lock al terminar la ejecución de los comandos. Si existe el archivo, el script simplemente envía un mensaje al usuario indicándole que ya el comando se está ejecutando.

Sin embargo, pudiera ocurrir, por una situación inesperada, que el archivo lock no se elimine pudiendo dejar efectos indeseados. La solución es bien sencilla:

LOCK="/var/run/rsync.lock"

if [ ! -e $LOCK ]; then
  trap "rm -f $LOCK; exit" INT TERM EXIT
  touch $LOCK
  rsync -av foo bar
  rm $LOCK
  trap - INT TERM EXIT
else
   echo "rsync ya se está ejecutando"
fi

La particularidad de esta solución es que el comando está encerrado en un trap, de modo que cuando se recibe una señal INT, TERM o EXITel script se detiene y elimina el archivo lock.

Vale la pena decir que pudiera darse una situación de competencia entre el tiempo en que se verifica el archivo lock y el tiempo en que este se crea. Una posible solución sería usar una redirección y el modo noclobber de bash el cual no redirige a un archivo existente:

LOCK="/var/run/rsync.lock"

if (set -o noclobber; echo $$ > "$LOCK") 2> /dev/null;
then
  trap 'rm -f "$LOCK"; exit $?' INT TERM EXIT
  rsync -av foo bar
  rm -f $LOCK
  trap - INT TERM EXIT
  else
    echo "rsync ya se está ejecutando: $(cat $LOCK)"
fi

La particularidad de este último es que se usa como ya se había dicho, el modo noclobber y que el archivo lock contiene el PID del proceso que se ejecuta.

También vale la pena mencionar que existen otras soluciones como flock o solo, sin embargo en esta entrada quise compartir las soluciones con recursos propios de bash.

¿De cuánta utilidad te ha parecido este contenido?

Yoel Torres

Share
Published by
Yoel Torres
Tags: bashtrap

Recent Posts

SquidStat, analizador de logs de squid diferente y 100% cubano

Me complace anunciar la creación de esta útil herramienta (SquidStats), para el análisis y monitoreo…

2 meses ago

n8n Transformando la Automatización de Flujos de Trabajo con Inteligencia Artificial

La inteligencia artificial está revolucionando las industrias al automatizar tareas, predecir patrones y permitiendo tomar…

2 meses ago

Alta disponibilidad de sus base de datos con Percona XtraDB Cluster en Kubernetes

Uno de los grandes retos al que nos podemos enfrentar cuando una aplicación crece, es…

2 años ago

Home automation (Parte 3) – ESPHome

Qué es lo que deseo hacer en este capítulo? Básicamente un sonoff, quiero encender/apagar las…

2 años ago

Home automation (Parte 2) – Home Assistant

Hace algunos meses estoy escuchando hablar del proyecto Home Assistant (HA). En palabras literales del…

2 años ago