Une solution ne nécessitant pas d'outils supplémentaires serait préférable.
ln -s my.pid .lock
réclamera le verrou (suivi de echo $$ > my.pid
) et en cas d'échec peut vérifier si le PID stocké dans .lock
est vraiment une instance active du script
Une solution ne nécessitant pas d'outils supplémentaires serait préférable.
ln -s my.pid .lock
réclamera le verrou (suivi de echo $$ > my.pid
) et en cas d'échec peut vérifier si le PID stocké dans .lock
est vraiment une instance active du script
Réponses:
Presque comme la réponse de nsg: utilisez un répertoire de verrouillage . La création de répertoire est atomique sous linux et unix et * BSD et beaucoup d'autres OS.
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
Vous pouvez mettre le PID du sh de verrouillage dans un fichier dans le répertoire de verrouillage à des fins de débogage, mais ne tombez pas dans le piège de penser que vous pouvez vérifier ce PID pour voir si le processus de verrouillage s'exécute toujours. Beaucoup de conditions de course se trouvent sur cette voie.
mkdir
n'est pas atomique sur NFS (ce qui n'est pas le cas pour moi, mais je suppose que l'on devrait le mentionner, si c'est vrai)
/tmp
lequel n'est généralement pas partagé NFS, donc ça devrait aller.
rm -rf
pour supprimer le répertoire de verrouillage. rmdir
échouera si quelqu'un (pas nécessairement vous) a réussi à ajouter un fichier dans le répertoire.
Pour ajouter à la réponse de Bruce Ediger , et inspiré par cette réponse , vous devez également ajouter plus d'intelligence au nettoyage pour éviter la terminaison de 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
.
Cela peut être trop simpliste, veuillez me corriger si je me trompe. N'est-ce pas ps
assez 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 $$
. exemples réels: ancien - 14532, nouveau - 1453, ancien - 28858, nouveau - 858.
grep -v $$
pourgrep -v "^${$} "
grep -wv "^$$"
(voir modifier).
J'utiliserais un fichier de verrouillage, comme mentionné par 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
n'est pas mauvaise non plus?
$$
dans le fichier de verrouillage. Ensuite, sleep
pendant un court intervalle et relisez-le. Si le PID est toujours le vôtre, vous avez réussi à acquérir le verrou. Ne nécessite absolument aucun outil supplémentaire.
Si vous voulez vous assurer qu'une seule instance de votre script est en cours d'exécution, jetez un œil à:
Verrouillez votre script (contre l'exécution parallèle)
Sinon, vous pouvez vérifier ps
ou invoquer lsof <full-path-of-your-script>
, car je ne les appellerais pas des outils supplémentaires.
Supplément :
en fait j'ai pensé à faire comme ça:
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
cela garantit que seul le processus avec le plus bas pid
continue de fonctionner même si vous forkez et exécutez plusieurs instances de <your_script>
simultanément.
[[(lsof $0 | wc -l) > 2]] && exit
pourrait être suffisant, ou est-ce aussi sujet aux conditions de course?
Une autre façon de vous assurer qu'une seule instance du script bash s'exécute:
#!/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
obtient le PID du script existant s'il est déjà en cours d'exécution ou se termine avec le code d'erreur 1 si aucun autre script n'est en cours d'exécution
Bien que vous ayez demandé une solution sans outils supplémentaires, c'est ma façon préférée d'utiliser flock
:
#!/bin/sh
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :
echo "servus!"
sleep 10
Cela vient de la section des exemples de man flock
, qui explique plus en détail:
Il s'agit d'un code passe-partout utile pour les scripts shell. Placez-le en haut du script shell que vous souhaitez verrouiller et il se verrouillera automatiquement lors de la première exécution. Si env var $ FLOCKER n'est pas défini sur le script shell en cours d'exécution, exécutez flock et récupérez un verrou exclusif non bloquant (en utilisant le script lui-même comme fichier de verrouillage) avant de le réexécuter avec les bons arguments. Il définit également la variable env FLOCKER à la bonne valeur afin qu'il ne s'exécute plus.
Points à considérer:
flock
, l'exemple de script se termine par une erreur s'il est introuvableVoir également /programming/185451/quick-and-dirty-way-to-ensure-only-one-instance-of-a-shell-script-is-running-at .
Il s'agit d'une version modifiée de la réponse d' Anselmo . L'idée est de créer un descripteur de fichier en lecture seule à l'aide du script bash lui-même et à utiliser flock
pour gérer le verrou.
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 principale différence avec toutes les autres réponses est que ce code ne modifie pas le système de fichiers, utilise un encombrement très faible et n'a pas besoin de nettoyage puisque le descripteur de fichier est fermé dès que le script se termine indépendamment de l'état de sortie. Ainsi, peu importe si le script échoue ou réussit.
exec 6< "$SCRIPT"
.
J'utilise cksum pour vérifier que mon script exécute vraiment une seule instance, même si je change le nom et le chemin du fichier .
Je n'utilise pas le fichier trap & lock, car si mon serveur tombe soudainement en panne, je dois supprimer le fichier de verrouillage manuellement après la montée du serveur.
Remarque: #! / Bin / bash en première ligne est requis pour 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
Ou vous pouvez coder en dur le cksum dans votre script, donc ne vous inquiétez plus si vous voulez changer le nom de fichier, le chemin ou le contenu de votre script .
#!/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
et fsum
, lorsque vous ne parlez plus de somme de contrôle.
Vous pouvez utiliser ceci: https://github.com/sayanarijit/pidlock
sudo pip install -U pidlock
pidlock -n sleepy_script -c 'sleep 10'
Mon code pour toi
#!/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}
Basé sur man flock
, améliorant uniquement:
executing
Où je mets ici le sleep 10
, vous pouvez mettre tout le script principal.