GNU/Linux >> Tutoriales Linux >  >> Linux

¿Bloqueo correcto en scripts de Shell?

A veces, debe asegurarse de que solo se esté ejecutando una instancia de un script de shell al mismo tiempo.

Por ejemplo, un trabajo cron que se ejecuta a través de crond que no proporciona
bloqueo por sí solo (por ejemplo, el crond predeterminado de Solaris).

Un patrón común para implementar el bloqueo es un código como este:

#!/bin/sh
LOCK=/var/tmp/mylock
if [ -f $LOCK ]; then            # 'test' -> race begin
  echo Job is already running!
  exit 6
fi
touch $LOCK                      # 'set'  -> race end
# do some work
rm $LOCK

Por supuesto, dicho código tiene una condición de carrera. Hay una ventana de tiempo en la que la
ejecución de dos instancias puede avanzar después de la línea 3 antes de que una pueda
tocar el $LOCK archivo.

Para un trabajo cron esto no suele ser un problema porque tiene un intervalo de
minutos entre dos invocaciones.

Pero las cosas pueden salir mal, por ejemplo, cuando el archivo de bloqueo está en un servidor NFS,
que se bloquea. En ese caso, varios trabajos cron pueden bloquearse en la línea 3 y ponerse en cola. Si
el servidor NFS vuelve a estar activo, entonces tiene una manada atronadora de trabajos
en ejecución paralelos.

Buscando en la web encontré la herramienta lockrun que parece una buena
solución a ese problema. Con él, ejecuta un script que necesita bloqueo como
este:

$ lockrun --lockfile=/var/tmp/mylock myscript.sh

Puede poner esto en un envoltorio o usarlo desde su crontab.

Utiliza lockf() (POSIX) si está disponible y recurre a flock() (BSD). Y lockf() el soporte para NFS debería estar relativamente extendido.

¿Existen alternativas a lockrun? ?

¿Qué pasa con otros demonios cron? ¿Existen crond comunes que admitan el bloqueo de una
forma sensata? Una mirada rápida a la página del manual de Vixie Crond (predeterminada en
sistemas Debian/Ubuntu) no muestra nada sobre el bloqueo.

¿Sería una buena idea incluir una herramienta como lockrun? en coreutils?

En mi opinión implementa un tema muy similar a timeout , nice y amigos.

Respuesta aceptada:

Aquí hay otra forma de bloquear el script de shell que puede evitar la condición de carrera que describió anteriormente, donde dos trabajos pueden pasar la línea 3. El noclobber La opción funcionará en ksh y bash. No uses set noclobber porque no debería estar escribiendo en csh/tcsh. 😉

lockfile=/var/tmp/mylock

if ( set -o noclobber; echo "$$" > "$lockfile") 2> /dev/null; then

        trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT

        # do stuff here

        # clean up after yourself, and release your trap
        rm -f "$lockfile"
        trap - INT TERM EXIT
else
        echo "Lock Exists: $lockfile owned by $(cat $lockfile)"
fi

YMMV con bloqueo en NFS (ya sabe, cuando no se puede acceder a los servidores NFS), pero en general es mucho más robusto de lo que solía ser. (hace 10 años)

Si tiene trabajos cron que hacen lo mismo al mismo tiempo, desde varios servidores, pero solo necesita 1 instancia para ejecutarse, algo como esto podría funcionar para usted.

Relacionado:¿comando simple de Shell de alternancia de Bluetooth de MacOS?

No tengo experiencia con lockrun, pero tener un entorno de bloqueo preestablecido antes de que el script se ejecute realmente podría ayudar. O puede que no. Simplemente está configurando la prueba para el archivo de bloqueo fuera de su secuencia de comandos en un contenedor y, en teoría, ¿no podría simplemente alcanzar la misma condición de carrera si dos trabajos fueron llamados por lockrun exactamente al mismo tiempo, al igual que con el 'interior- ¿la solución del guión?

El bloqueo de archivos respeta el comportamiento del sistema de todos modos, y cualquier secuencia de comandos que no verifique la existencia del archivo de bloqueo antes de ejecutarse hará lo que sea que vaya a hacer. Con solo realizar la prueba del archivo de bloqueo y el comportamiento adecuado, resolverá el 99 % de los problemas potenciales, si no el 100 %.

Si te encuentras mucho con las condiciones de carrera del archivo de bloqueo, puede ser un indicador de un problema mayor, como no tener tus trabajos programados correctamente, o quizás si el intervalo no es tan importante como la finalización del trabajo, tal vez tu trabajo sea más adecuado para ser demonizado. .

EDITAR A CONTINUACIÓN:2016-05-06 (si está utilizando KSH88)

Basado en el comentario de @Clint Pachl a continuación, si usa ksh88, use mkdir en lugar de noclobber . Esto mitiga principalmente una posible condición de carrera, pero no la limita por completo (aunque el riesgo es minúsculo). Para obtener más información, lea el enlace que Clint publicó a continuación.

lockdir=/var/tmp/mylock
pidfile=/var/tmp/mylock/pid

if ( mkdir ${lockdir} ) 2> /dev/null; then
        echo $$ > $pidfile
        trap 'rm -rf "$lockdir"; exit $?' INT TERM EXIT
        # do stuff here

        # clean up after yourself, and release your trap
        rm -rf "$lockdir"
        trap - INT TERM EXIT
else
        echo "Lock Exists: $lockdir owned by $(cat $pidfile)"
fi

Y, como ventaja adicional, si necesita crear archivos tmp en su secuencia de comandos, puede usar el lockdir directorio para ellos, sabiendo que se limpiarán cuando finalice el script.

Para fiestas más modernas, el método noclobber en la parte superior debería ser adecuado.


Linux
  1. ¿Permitir Setuid en scripts de Shell?

  2. ¿Ejecutar scripts de Shell a través de un sitio web?

  3. ¿Extensiones de archivo para secuencias de comandos de Unix Shell?

  4. ¿Compartir variables entre múltiples scripts de shell?

  5. ¿Cómo probar el cumplimiento de Posix de los scripts de Shell?

Cómo crear scripts de shell

¿Ocultar la contraseña en los scripts de Shell?

Matrices en scripts de Shell

¿Cómo usar if-else en Shell Scripts?

Comprender el bucle for en los scripts de Shell

El bucle while en los scripts de Shell