Pregunta Evitar la ejecución de trabajos cron duplicados


He programado un trabajo cron para ejecutar cada minuto, pero a veces la secuencia de comandos tarda más de un minuto en finalizar y no quiero que los trabajos comiencen a "apilarse" unos sobre otros. Supongo que este es un problema de concurrencia, es decir, la ejecución del script debe ser mutuamente excluyente.

Para resolver el problema, hice que el script buscara la existencia de un archivo en particular ("lockfile.txt") y salir si existe o touch si no lo hace. ¡Pero este es un semáforo bastante malo! ¿Hay alguna mejor práctica que debería saber? ¿Debería haber escrito un demonio en su lugar?


81
2017-11-09 11:32


origen




Respuestas:


Hay un par de programas que automatizan esta función, eliminan la molestia y los posibles errores de hacerlo usted mismo, y evitan el problema del bloqueo obsoleto al usar bandada detrás de escena (lo que es un riesgo si solo está usando el toque) . he usado lockrun y lckdo en el pasado, pero ahora hay flock(1) (en versiones nuevas de util-linux) que es genial. Es muy fácil de usar:

* * * * * /usr/bin/flock -n /tmp/fcj.lockfile /usr/local/bin/frequent_cron_job

109
2017-11-09 11:57



lckdo se eliminará de moreutils, ahora que flock (1) está en util-linux. Y ese paquete es básicamente obligatorio en los sistemas Linux, por lo que debería poder confiar en su presencia. Para uso, mira abajo. - jldugger
Sí, el rebaño es ahora mi opción preferida. Incluso voy a actualizar mi respuesta para adaptarse. - womble♦
¿Alguien sabe la diferencia entre flock -n file command y flock -n file -c command ? - Nanne
@Nanne, tendría que revisar el código para estar segura, pero mi conjetura es que -c ejecuta el comando especificado a través de un shell (según la página del manual), mientras que el comando "bare" (no-c) forma solo execs la orden dada. Poner algo a través del shell le permite hacer cosas como shell (como ejecutar varios comandos separados con ; o &&), pero también te abre a ataques de expansión de shell si estás usando una entrada que no es de confianza. - womble♦
Fue un argumento a la (hipotética). frequent_cron_job El comando que intentaba mostrarlo se estaba ejecutando cada minuto. Lo quité porque no agregó nada útil y causó confusión (la suya, si nadie más lo ha hecho en los últimos años). - womble♦


La mejor manera en shell es usar rebaño (1)

(
  flock -x -w 5 99
  ## Do your stuff here
) 99>/path/to/my.lock

27
2017-11-09 11:45



No puedo dejar de votar un uso complicado de la redirección de fd. Es simplemente demasiado impresionante. - womble♦
No analiza para mí en Bash o ZSH, necesito eliminar el espacio entre 99 y > así es 99> /... - Kyle Brandt♦
@Javier: No significa que no sea complicado y arcano, solo que es documentado, complicado, y arcano. - womble♦
¿Qué pasaría si reinicia mientras esto se está ejecutando o si el proceso se detiene de alguna manera? ¿Estaría encerrado para siempre entonces? - Alex R
Entiendo que esta estructura crea un bloqueo exclusivo, pero no entiendo la mecánica de cómo se logra esto. ¿Cuál es la función del '99' en esta respuesta? ¿Alguien quiere explicar esto por favor? ¡Gracias! - Asciiom


Actualmente, flock -n puede ser usado en lugar de lckdo*, por lo que usarás el código de los desarrolladores del kernel.

Sobre la base de el ejemplo de womble, escribirías algo como:

* * * * * flock -n /some/lockfile command_to_run_every_minute

Por cierto, mirando el código, todos flock, lockruny lckdo haga exactamente lo mismo, por lo que es solo una cuestión de la que está más disponible para usted.

* Dado que, dada mi reputación en el momento de escribir, no puedo editar ni comentar las respuestas anteriores, tengo que escribir esto como una respuesta por separado.


21
2017-11-19 22:43





Puede utilizar un archivo de bloqueo. Cree este archivo cuando se inicie el script y elimínelo cuando finalice. El script, antes de ejecutar su rutina principal, debe verificar si el archivo de bloqueo existe y proceder en consecuencia.

Los archivos de bloqueo son utilizados por initscripts y por muchas otras aplicaciones y utilidades en los sistemas Unix.


2
2017-11-09 11:36



este es el solamente Así lo he visto implementado, personalmente. Uso según la sugerencia del mantenedor como espejo para un proyecto OSS - warren


Esto también podría ser una señal de que estás haciendo lo incorrecto. Si sus trabajos se ejecutan con tanta frecuencia y con tanta frecuencia, tal vez debería considerar la posibilidad de eliminarlo y convertirlo en un programa de tipo demonio.


1
2017-11-09 11:45



Estoy totalmente en desacuerdo con esto. Si tiene algo que debe ejecutarse periódicamente, convertirlo en un demonio es una solución "martillo para una tuerca". Utilizar un archivo de bloqueo para evitar accidentes es una solución perfectamente razonable. Nunca tuve problemas para usarlo. - womble♦
@womble estoy de acuerdo; ¡Pero me gusta romper nueces con mazos! :-) - wzzrd


No ha especificado si desea que la secuencia de comandos espere a que se complete o no la ejecución anterior. Al "No quiero que los trabajos comiencen" acumulándose "uno sobre el otro", supongo que está insinuando que desea que el script se cierre si ya se está ejecutando,

Entonces, si no quieres depender de lckdo o similar, puedes hacer esto:


PIDFILE=/tmp/`basename $0`.pid

if [ -f $PIDFILE ]; then
  if ps -p `cat $PIDFILE` > /dev/null 2>&1; then
      echo "$0 already running!"
      exit
  fi
fi
echo $$ > $PIDFILE

trap 'rm -f "$PIDFILE" >/dev/null 2>&1' EXIT HUP KILL INT QUIT TERM

# do the work


1
2017-11-09 14:33



Gracias, su ejemplo es útil: quiero que el script se cierre si ya se está ejecutando. Gracias por mencionar ickdo - Parece hacer el truco. - Tom


Su daemon cron no debería invocar trabajos si todavía se están ejecutando instancias anteriores de ellos. Soy el desarrollador de un cron daemon. dcron, y específicamente tratamos de evitar eso. No sé cómo Vixie Cron u otros demonios manejan esto.


1
2018-02-17 15:59