Se preferiría una solución que no requiera herramientas adicionales.
ln -s my.pid .lock
reclamar el bloqueo (seguido de echo $$ > my.pid
) y en caso de falla puede verificar si el PID almacenado .lock
es realmente una instancia activa del script
Se preferiría una solución que no requiera herramientas adicionales.
ln -s my.pid .lock
reclamar el bloqueo (seguido de echo $$ > my.pid
) y en caso de falla puede verificar si el PID almacenado .lock
es realmente una instancia activa del script
Respuestas:
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 $LOCKDIR
then
# Do important, exclusive stuff
if rmdir $LOCKDIR
then
echo "Victory is mine"
else
echo "Could not remove lock dir" >&2
fi
else
# 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.
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:
#Remove the lock directory
function cleanup {
if rmdir $LOCKDIR; then
echo "Finished"
else
echo "Failed to remove lock directory '$LOCKDIR'"
exit 1
fi
}
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 here
else
echo "Could not create lock directory '$LOCKDIR'"
exit 1
fi
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 ?
#!/bin/bash
me="$(basename "$0")";
running=$(ps h -C "$me" | grep -wv $$ | wc -l);
[[ $running > 1 ]] && exit;
# do stuff below this comment
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
#!/bin/bash
# Exit if /tmp/lock.file exists
[ -f /tmp/lock.file ] && exit
# Create lock file, sleep 1 sec and verify lock
echo $$ > /tmp/lock.file
sleep 1
[ "x$(cat /tmp/lock.file)" == "x"$$ ] || exit
# Do stuff
sleep 60
# Remove lock file
rm /tmp/lock.file
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 invocar lsof <full-path-of-your-script>
, ya que no los llamaría herramientas adicionales.
Suplemento :
en realidad pensé en hacerlo así:
for LINE in `lsof -c <your_script> -F p`; do
if [ $$ -gt ${LINE#?} ] ; then
echo "'$0' is already running" 1>&2
exit 1;
fi
done
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.
[[(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:
#!/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
Aunque ha pedido una solución sin herramientas adicionales, esta es mi forma favorita de usar flock
:
#!/bin/sh
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :
echo "servus!"
sleep 10
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:
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 .
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.
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.
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
#!/bin/bash
checkinstance(){
nprog=0
mysum=$(cksum $0|awk '{print $1}')
for i in `ps -ef |grep /bin/bash|awk '{print $2}'`;do
proc=$(ls -lha /proc/$i/exe 2> /dev/null|grep bash)
if [[ $? -eq 0 ]];then
cmd=$(strings /proc/$i/cmdline|grep -v bash)
if [[ $? -eq 0 ]];then
fsum=$(cksum /proc/$i/cwd/$cmd|awk '{print $1}')
if [[ $mysum -eq $fsum ]];then
nprog=$(($nprog+1))
fi
fi
fi
done
if [[ $nprog -gt 1 ]];then
echo $0 is already running.
exit
fi
}
checkinstance
#--- run your script bellow
echo pass
while true;do sleep 1000;done
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 .
#!/bin/bash
mysum=1174212411
checkinstance(){
nprog=0
for i in `ps -ef |grep /bin/bash|awk '{print $2}'`;do
proc=$(ls -lha /proc/$i/exe 2> /dev/null|grep bash)
if [[ $? -eq 0 ]];then
cmd=$(strings /proc/$i/cmdline|grep -v bash)
if [[ $? -eq 0 ]];then
fsum=$(grep mysum /proc/$i/cwd/$cmd|head -1|awk -F= '{print $2}')
if [[ $mysum -eq $fsum ]];then
nprog=$(($nprog+1))
fi
fi
fi
done
if [[ $nprog -gt 1 ]];then
echo $0 is already running.
exit
fi
}
checkinstance
#--- run your script bellow
echo pass
while true;do sleep 1000;done
mysum
y fsum
, cuando ya no esté hablando de una suma de verificación.
Puede usar esto: https://github.com/sayanarijit/pidlock
sudo pip install -U pidlock
pidlock -n sleepy_script -c 'sleep 10'
Mi código para ti
#!/bin/bash
script_file="$(/bin/readlink -f $0)"
lock_file=${script_file////_}
function executing {
echo "'${script_file}' already executing"
exit 1
}
(
flock -n 9 || executing
sleep 10
) 9> /var/lock/${lock_file}
Basado en man flock
, mejorando solo:
executing
Donde puse aquí sleep 10
, puedes poner todo el guión principal.