BASH – Archivo de bloqueo.-

Evitar problemas de concurrencia

Si dos copias del script son iniciadas al mismo tiempo, es posible (aunque difícil, estas operaciones duran pocos milisegundos) que las dos copias lleguen al mismo tiempo al código que comprueba que el archivo $LOCK_FILE exista. Si esto sucede, ambas copias del script pueden determinar que el archivo no existe, y continuar su ejecución.

Esta clase de problemas en donde dos programas compiten por un recurso, sin que podamos determinar cuál de ellos lo obtiene, se conocen como race conditions (“condiciones de carrera”). En muchos casos, cuando es posible, los evitamos usando operaciones atómicas [3].

Para este problema en particular, veremos dos variantes.

1.mkdir

Con mkdir tenemos una solución al problema, siempre que estemos dispuestos a usar un directorio y no un archivo como $LOCK_FILE. La orden

mkdir $LOCK_DIR

tiene dos resultados posibles: si el directorio no existe, será creado, y mkdir saldrá con un código de éxito. Si el directorio existe, mkdir fallará y no habrá cambios en el sistema.

2.noclobber

BASH tiene una opción llamada noclobber, que hace que si intentamos redirigir salida (vía >) a un archivo que ya existe, la redirección falle. Basta entonces con escribir algo como:

set -o noclobber # se puede abreviar como set -C
: > $LOCK_FILE
if [[ $? != 0 ]];
    # la redirección falló, el archivo existía previamente
fi

Para desactivar noclobber (como hacemos con las opciones de bash que modifican su comportamiento y usamos para una parte específica de nuestro script), debemos añadir una línea conteniendo set +C. Otra opción es utilizar una subshell, de tal manera que la shell que corre nuestro script no se vea afectada por el cambio, y por tanto no sea necesario desactivar nada:

(set -C; : > $LOCK_FILE) 2> /dev/null
if [[ $? != 0 ]]; then
    # la redirección falló, el archivo existía
fi

Con estas mejoras, así es como nuestro script se ve ahora:

#!/bin/bash
LOCK_FILE=/tmp/script.fulanito.lock
trap 'rm $LOCK_FILE; exit $?' EXIT
( set -C; : > $LOCK_FILE ) 2>/dev/null
if [[ $? == 0 ]]; then
   touch $LOCK_FILE
   #contenido del script
else
   echo "Una copia anterior de este script sigue corriendo"
fi
Esta entrada fue publicada en Unix - Linux. Guarda el enlace permanente.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.