Quel est le moyen le plus rapide de s'assurer qu'une seule instance d'un shell script est en cours d'exécution à un moment donné ?
Réponses
Trop de publicités?En ciblant une machine Debian, je trouve le lockfile-progs
pour être une bonne solution. procmail
est également accompagné d'un lockfile
outil. Cependant, il m'arrive parfois de n'avoir ni l'un ni l'autre.
Voici ma solution qui utilise mkdir
pour l'atomicité et un fichier PID pour détecter les verrous périmés. Ce code est actuellement en production sur une installation Cygwin et fonctionne bien.
Pour l'utiliser, il suffit d'appeler exclusive_lock_require
quand vous avez besoin d'un accès exclusif à quelque chose. Un paramètre facultatif de nom de verrou vous permet de partager les verrous entre différents scripts. Il existe également deux fonctions de niveau inférieur ( exclusive_lock_try
y exclusive_lock_retry
) si vous avez besoin de quelque chose de plus complexe.
function exclusive_lock_try() # [lockname]
{
local LOCK_NAME="${1:-`basename $0`}"
LOCK_DIR="/tmp/.${LOCK_NAME}.lock"
local LOCK_PID_FILE="${LOCK_DIR}/${LOCK_NAME}.pid"
if [ -e "$LOCK_DIR" ]
then
local LOCK_PID="`cat "$LOCK_PID_FILE" 2> /dev/null`"
if [ ! -z "$LOCK_PID" ] && kill -0 "$LOCK_PID" 2> /dev/null
then
# locked by non-dead process
echo "\"$LOCK_NAME\" lock currently held by PID $LOCK_PID"
return 1
else
# orphaned lock, take it over
( echo $$ > "$LOCK_PID_FILE" ) 2> /dev/null && local LOCK_PID="$$"
fi
fi
if [ "`trap -p EXIT`" != "" ]
then
# already have an EXIT trap
echo "Cannot get lock, already have an EXIT trap"
return 1
fi
if [ "$LOCK_PID" != "$$" ] &&
! ( umask 077 && mkdir "$LOCK_DIR" && umask 177 && echo $$ > "$LOCK_PID_FILE" ) 2> /dev/null
then
local LOCK_PID="`cat "$LOCK_PID_FILE" 2> /dev/null`"
# unable to acquire lock, new process got in first
echo "\"$LOCK_NAME\" lock currently held by PID $LOCK_PID"
return 1
fi
trap "/bin/rm -rf \"$LOCK_DIR\"; exit;" EXIT
return 0 # got lock
}
function exclusive_lock_retry() # [lockname] [retries] [delay]
{
local LOCK_NAME="$1"
local MAX_TRIES="${2:-5}"
local DELAY="${3:-2}"
local TRIES=0
local LOCK_RETVAL
while [ "$TRIES" -lt "$MAX_TRIES" ]
do
if [ "$TRIES" -gt 0 ]
then
sleep "$DELAY"
fi
local TRIES=$(( $TRIES + 1 ))
if [ "$TRIES" -lt "$MAX_TRIES" ]
then
exclusive_lock_try "$LOCK_NAME" > /dev/null
else
exclusive_lock_try "$LOCK_NAME"
fi
LOCK_RETVAL="${PIPESTATUS[0]}"
if [ "$LOCK_RETVAL" -eq 0 ]
then
return 0
fi
done
return "$LOCK_RETVAL"
}
function exclusive_lock_require() # [lockname] [retries] [delay]
{
if ! exclusive_lock_retry "$@"
then
exit 1
fi
}
Si les limitations de flock, qui ont déjà été décrites ailleurs dans ce fil, ne sont pas un problème pour vous, alors cela devrait fonctionner :
#!/bin/bash
{
# exit if we are unable to obtain a lock; this would happen if
# the script is already running elsewhere
flock -x -n 100 || exit
# put commands to run here
sleep 100
} 100>/tmp/myjob.lock
Certains unixes ont lockfile
qui est très similaire à celui déjà mentionné flock
.
De la page de manuel :
lockfile peut être utilisé pour créer un ou plusieurs fichiers sémaphores. Si lock- file ne peut pas créer tous les fichiers fichiers spécifiés (dans l'ordre spécifié), il attend le temps de sommeil (par défaut 8) secondes et tente à nouveau le dernier fichier qui qui n'a pas réussi. Vous pouvez spécifier le nombre de tentatives à effectuer jusqu'à ce que l'échec soit retourné. Si le nombre de de tentatives est -1 (par défaut, c'est-à-dire -r-1), lockfile essaiera indéfiniment.