@ jw013 gracias! Entonces, tal vez algo como ln -s my.pid .lockreclamar el bloqueo (seguido de echo $$ > my.pid) y en caso de falla puede verificar si el PID almacenado .lockes realmente una instancia activa del script
Tobias Kienzler,
Respuestas:
18
Casi como la respuesta de nsg: use un directorio de bloqueo . La creación de directorios es atómica en Linux y Unix y * BSD y muchos otros sistemas operativos.
if mkdir $LOCKDIRthen# Do important, exclusive stuffif rmdir $LOCKDIRthen
echo "Victory is mine"else
echo "Could not remove lock dir">&2fielse# Handle error condition...fi
Puede poner el PID del bloqueo sh en un archivo en el directorio de bloqueo para fines de depuración, pero no caiga en la trampa de pensar que puede verificar ese PID para ver si el proceso de bloqueo aún se ejecuta. Muchas condiciones de carrera se encuentran en ese camino.
Consideraría usar el PID almacenado para verificar si la instancia de bloqueo aún está activa. Sin embargo, aquí hay una afirmación que mkdirno es atómica en NFS (que no es el caso para mí, pero creo que uno debería mencionar eso, si es cierto)
Tobias Kienzler
Sí, utilice el PID almacenado para ver si el proceso de bloqueo aún se ejecuta, pero no intente hacer nada más que registrar un mensaje. El trabajo de verificar el pid almacenado, crear un nuevo archivo PID, etc., deja una gran ventana para las carreras.
Bruce Ediger
Ok, como dijo Ihunath , lo más probable es /tmpque el lockdir esté en el que generalmente no se comparte NFS, por lo que debería estar bien.
Tobias Kienzler
Lo usaría rm -rfpara eliminar el directorio de bloqueo. rmdirfallará si alguien (no necesariamente usted) logró agregar un archivo al directorio.
chepner
18
Para agregar a la respuesta de Bruce Ediger , e inspirado por esta respuesta , también debe agregar más inteligencia a la limpieza para evitar la terminación del script:
#Remove the lock directoryfunction cleanup {if rmdir $LOCKDIR;then
echo "Finished"else
echo "Failed to remove lock directory '$LOCKDIR'"
exit 1fi}if mkdir $LOCKDIR;then#Ensure that if we "grabbed a lock", we release it#Works for SIGTERM and SIGINT(Ctrl-C)
trap "cleanup" EXIT
echo "Acquired lock, running"# Processing starts hereelse
echo "Could not create lock directory '$LOCKDIR'"
exit 1fi
He usado esta condición durante una semana, y en 2 ocasiones no evitó que se iniciara un nuevo proceso. Me di cuenta de cuál es el problema: el nuevo pid es una subcadena del antiguo y se oculta grep -v $$. ejemplos reales: antiguo - 14532, nuevo - 1453, antiguo - 28858, nuevo - 858.
Naktibalda
Lo arreglé cambiando grep -v $$agrep -v "^${$} "
Naktibalda
@Naktibalda buena captura, gracias! También puedes arreglarlo con grep -wv "^$$"(ver edición).
terdon
Gracias por esa actualización Mi patrón ocasionalmente fallaba porque los pids más cortos se dejaban rellenados con espacios.
Naktibalda
4
Usaría un archivo de bloqueo, como lo menciona Marco
(Creo que olvidó crear el archivo de bloqueo) ¿Qué pasa con las condiciones de carrera ?
Tobias Kienzler
ops :) Sí, las condiciones de carrera son un problema en mi ejemplo, generalmente escribo trabajos cron por hora o por día y las condiciones de carrera son raras.
nsg
Tampoco deberían ser relevantes en mi caso, pero es algo que uno debe tener en cuenta. ¿Quizás usar lsof $0no es malo tampoco?
Tobias Kienzler
Puede disminuir la condición de carrera escribiendo su $$en el archivo de bloqueo. Luego, sleeppor un breve intervalo y léelo de nuevo. Si el PID sigue siendo suyo, adquirió el bloqueo con éxito. No necesita absolutamente ninguna herramienta adicional.
manatwork
1
Nunca he usado lsof para este propósito, esto debería funcionar. Tenga en cuenta que lsof es realmente lento en mi sistema (1-2 segundos) y lo más probable es que haya mucho tiempo para las condiciones de carrera.
nsg
3
Si quieres asegurarte de que solo se esté ejecutando una instancia de tu script, mira:
Gracias por el enlace, pero ¿podría incluir las partes esenciales en su respuesta? Es una política común en SE evitar la pudrición de enlaces ... Pero algo como esto [[(lsof $0 | wc -l) > 2]] && exitpodría ser suficiente, ¿o esto también es propenso a las condiciones de carrera?
Tobias Kienzler
Tienes razón, faltaba la parte esencial de mi respuesta y solo publicar enlaces es bastante lamentable. Agregué mi propia sugerencia a la respuesta.
user1146332
3
Otra forma de asegurarse de que se ejecute una sola instancia de script bash:
#!/bin/bash# Check if another instance of script is running
pidof -o %PPID -x $0 >/dev/null && echo "ERROR: Script $0 already running"&& exit 1...
pidof -o %PPID -x $0 obtiene el PID del script existente si ya se está ejecutando o sale con el código de error 1 si no se está ejecutando ningún otro script
Esto viene de la sección de ejemplos de man flock, que explica además:
Este es un código repetitivo útil para los scripts de shell. Póngalo en la parte superior del script de shell que desea bloquear y se bloqueará automáticamente en la primera ejecución. Si env var $ FLOCKER no está configurado para el script de shell que se está ejecutando, ejecute flock y tome un bloqueo exclusivo sin bloqueo (usando el script en sí como el archivo de bloqueo) antes de volver a ejecutarlo con los argumentos correctos. También establece FLOCKER env var en el valor correcto para que no vuelva a ejecutarse.
Puntos a considerar:
Requiere flock, el script de ejemplo termina con un error si no se puede encontrar
Esta es una versión modificada de la Respuesta de Anselmo . La idea es crear un descriptor de archivo de solo lectura usando el script bash y usarlo flockpara manejar el bloqueo.
SCRIPT=`realpath $0`# get absolute path to the script itself
exec 6<"$SCRIPT"# open bash script using file descriptor 6
flock -n 6||{ echo "ERROR: script is already running"&& exit 1;}# lock file descriptor 6 OR show error message if script is already running
echo "Run your single instance code here"
La principal diferencia con todas las demás respuestas es que este código no modifica el sistema de archivos, utiliza una huella muy baja y no necesita ninguna limpieza, ya que el descriptor de archivos se cierra tan pronto como el script finaliza independientemente del estado de salida. Por lo tanto, no importa si el script falla o tiene éxito.
Siempre debe citar todas las referencias de variables de shell a menos que tenga una buena razón para no hacerlo y esté seguro de saber lo que está haciendo. Entonces deberías estar haciendo exec 6< "$SCRIPT".
Scott
@ Scott, he cambiado el código según tus sugerencias. Muchas gracias.
John Doe
1
Estoy usando cksum para verificar que mi script realmente esté ejecutando una sola instancia, incluso cambio el nombre del archivo y la ruta del archivo .
No estoy usando trap & lock file, porque si mi servidor se apaga repentinamente, necesito eliminar manualmente el archivo de bloqueo después de que el servidor se enciende.
Nota: #! / Bin / bash en primera línea se requiere para grep ps
O puede codificar cksum dentro de su secuencia de comandos, de modo que no se preocupe nuevamente si desea cambiar el nombre de archivo, la ruta o el contenido de su secuencia de comandos .
Explique exactamente cómo codificar la suma de comprobación es una buena idea.
Scott
no codifica la suma de comprobación, solo crea la clave de identidad de su script, cuando se ejecuta otra instancia, verificará otro proceso de script de shell y capturará primero el archivo, si su clave de identidad está en ese archivo, entonces significa que su instancia ya se está ejecutando.
Arputra
OKAY; Por favor, editar su respuesta a explicar eso. Y, en el futuro, no publique múltiples bloques de código de 30 líneas que parezcan (casi) idénticos sin decir y explicar cómo son diferentes. Y no diga cosas como "puede codificar [sic] cksum dentro de su script", y no continúe usando nombres de variables mysumy fsum, cuando ya no esté hablando de una suma de verificación.
Scott
Parece interesante, gracias! Y bienvenidos a unix.stackexchange :)
ln -s my.pid .lock
reclamar el bloqueo (seguido deecho $$ > my.pid
) y en caso de falla puede verificar si el PID almacenado.lock
es realmente una instancia activa del scriptRespuestas:
Casi como la respuesta de nsg: use un directorio de bloqueo . La creación de directorios es atómica en Linux y Unix y * BSD y muchos otros sistemas operativos.
Puede poner el PID del bloqueo sh en un archivo en el directorio de bloqueo para fines de depuración, pero no caiga en la trampa de pensar que puede verificar ese PID para ver si el proceso de bloqueo aún se ejecuta. Muchas condiciones de carrera se encuentran en ese camino.
fuente
mkdir
no es atómica en NFS (que no es el caso para mí, pero creo que uno debería mencionar eso, si es cierto)/tmp
que el lockdir esté en el que generalmente no se comparte NFS, por lo que debería estar bien.rm -rf
para eliminar el directorio de bloqueo.rmdir
fallará si alguien (no necesariamente usted) logró agregar un archivo al directorio.Para agregar a la respuesta de Bruce Ediger , e inspirado por esta respuesta , también debe agregar más inteligencia a la limpieza para evitar la terminación del script:
fuente
if ! mkdir "$LOCKDIR"; then handle failure to lock and exit; fi trap and do processing after if-statement
.Esto puede ser demasiado simplista, corrígeme si me equivoco. ¿No es lo
ps
suficientemente simple ?fuente
grep -v $$
. ejemplos reales: antiguo - 14532, nuevo - 1453, antiguo - 28858, nuevo - 858.grep -v $$
agrep -v "^${$} "
grep -wv "^$$"
(ver edición).Usaría un archivo de bloqueo, como lo menciona Marco
fuente
lsof $0
no es malo tampoco?$$
en el archivo de bloqueo. Luego,sleep
por un breve intervalo y léelo de nuevo. Si el PID sigue siendo suyo, adquirió el bloqueo con éxito. No necesita absolutamente ninguna herramienta adicional.Si quieres asegurarte de que solo se esté ejecutando una instancia de tu script, mira:
Bloquee su script (contra ejecución paralela)
De lo contrario, puede verificar
ps
o invocarlsof <full-path-of-your-script>
, ya que no los llamaría herramientas adicionales.Suplemento :
en realidad pensé en hacerlo así:
esto asegura que solo el proceso con el más bajo
pid
siga ejecutándose incluso si bifurca y ejecuta varias instancias de forma<your_script>
simultánea.fuente
[[(lsof $0 | wc -l) > 2]] && exit
podría ser suficiente, ¿o esto también es propenso a las condiciones de carrera?Otra forma de asegurarse de que se ejecute una sola instancia de script bash:
pidof -o %PPID -x $0
obtiene el PID del script existente si ya se está ejecutando o sale con el código de error 1 si no se está ejecutando ningún otro scriptfuente
Aunque ha pedido una solución sin herramientas adicionales, esta es mi forma favorita de usar
flock
:Esto viene de la sección de ejemplos de
man flock
, que explica además:Puntos a considerar:
flock
, el script de ejemplo termina con un error si no se puede encontrarVer también /programming/185451/quick-and-dirty-way-to-ensure-only-one-instance-of-a-shell-script-is-running-at .
fuente
Esta es una versión modificada de la Respuesta de Anselmo . La idea es crear un descriptor de archivo de solo lectura usando el script bash y usarlo
flock
para manejar el bloqueo.La principal diferencia con todas las demás respuestas es que este código no modifica el sistema de archivos, utiliza una huella muy baja y no necesita ninguna limpieza, ya que el descriptor de archivos se cierra tan pronto como el script finaliza independientemente del estado de salida. Por lo tanto, no importa si el script falla o tiene éxito.
fuente
exec 6< "$SCRIPT"
.Estoy usando cksum para verificar que mi script realmente esté ejecutando una sola instancia, incluso cambio el nombre del archivo y la ruta del archivo .
No estoy usando trap & lock file, porque si mi servidor se apaga repentinamente, necesito eliminar manualmente el archivo de bloqueo después de que el servidor se enciende.
Nota: #! / Bin / bash en primera línea se requiere para grep ps
O puede codificar cksum dentro de su secuencia de comandos, de modo que no se preocupe nuevamente si desea cambiar el nombre de archivo, la ruta o el contenido de su secuencia de comandos .
fuente
mysum
yfsum
, cuando ya no esté hablando de una suma de verificación.Puede usar esto: https://github.com/sayanarijit/pidlock
fuente
Mi código para ti
Basado en
man flock
, mejorando solo:executing
Donde puse aquí
sleep 10
, puedes poner todo el guión principal.fuente