@ jw013 merci! Alors peut-être quelque chose comme ln -s my.pid .lockréclamera le verrou (suivi de echo $$ > my.pid) et en cas d'échec peut vérifier si le PID stocké dans .lockest vraiment une instance active du script
Tobias Kienzler
Réponses:
18
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 stuffif rmdir $LOCKDIR
then
echo "Victory is mine"else
echo "Could not remove lock dir">&2fielse# 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.
J'envisagerais d'utiliser le PID stocké pour vérifier si l'instance de verrouillage est toujours en vie. Cependant, voici une affirmation qui mkdirn'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)
Tobias Kienzler
Oui, utilisez certainement le PID stocké pour voir si le processus de verrouillage s'exécute toujours, mais n'essayez pas de faire autre chose que d'enregistrer un message. Le travail de vérification du pid stocké, de création d'un nouveau fichier PID, etc., laisse une grande fenêtre pour les courses.
Bruce Ediger
Ok, comme l' a dit Ihunath , le lockdir serait probablement dans /tmplequel n'est généralement pas partagé NFS, donc ça devrait aller.
Tobias Kienzler
J'utiliserais rm -rfpour 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.
chepner
18
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 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
J'ai utilisé cette condition pendant une semaine, et à 2 reprises, cela n'a pas empêché le démarrage d'un nouveau processus. J'ai compris quel était le problème - le nouveau pid est une sous-chaîne de l'ancien et est caché par grep -v $$. exemples réels: ancien - 14532, nouveau - 1453, ancien - 28858, nouveau - 858.
Naktibalda
Je l'ai corrigé en changeant grep -v $$pourgrep -v "^${$} "
Naktibalda
@Naktibalda bonne prise, merci! Vous pouvez également le corriger avec grep -wv "^$$"(voir modifier).
terdon
Merci pour cette mise à jour. Mon modèle a parfois échoué parce que les pids plus courts étaient remplis d'espaces.
Naktibalda
4
J'utiliserais un fichier de verrouillage, comme mentionné par Marco
(Je pense que vous avez oublié de créer le fichier de verrouillage) Qu'en est-il des conditions de course ?
Tobias Kienzler
ops :) Oui, les conditions de course sont un problème dans mon exemple, j'écris habituellement des tâches cron horaires ou quotidiennes et les conditions de course sont rares.
nsg
Ils ne devraient pas non plus être pertinents dans mon cas, mais c'est quelque chose qu'il faut garder à l'esprit. Peut-être que l'utilisation lsof $0n'est pas mauvaise non plus?
Tobias Kienzler
Vous pouvez diminuer la condition de concurrence en écrivant votre $$dans le fichier de verrouillage. Ensuite, sleeppendant 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.
manatwork
1
Je n'ai jamais utilisé lsof à cet effet, je pense que cela devrait fonctionner. Notez que lsof est vraiment lent dans mon système (1-2 sec) et très probablement il y a beaucoup de temps pour les conditions de course.
nsg
3
Si vous voulez vous assurer qu'une seule instance de votre script est en cours d'exécution, jetez un œil à:
Sinon, vous pouvez vérifier psou 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`;doif[ $$ -gt ${LINE#?} ] ; then
echo "'$0' is already running"1>&2
exit 1;fidone
cela garantit que seul le processus avec le plus bas pidcontinue de fonctionner même si vous forkez et exécutez plusieurs instances de <your_script>simultanément.
Merci pour le lien, mais pourriez-vous inclure les éléments essentiels dans votre réponse? C'est une politique commune au SE d'empêcher la pourriture des liens ... Mais quelque chose comme ça [[(lsof $0 | wc -l) > 2]] && exitpourrait être suffisant, ou est-ce aussi sujet aux conditions de course?
Tobias Kienzler
Vous avez raison, la partie essentielle de ma réponse manquait et seuls les liens de publication sont assez boiteux. J'ai ajouté ma propre suggestion à la réponse.
user1146332
3
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
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:
Requiert flock, l'exemple de script se termine par une erreur s'il est introuvable
Ne nécessite aucun fichier de verrouillage supplémentaire
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 flockpour 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.
Vous devez toujours citer toutes les références de variables de shell, sauf si vous avez une bonne raison de ne pas le faire, et vous êtes sûr de savoir ce que vous faites. Vous devriez donc faire exec 6< "$SCRIPT".
Scott
@Scott J'ai changé le code selon vos suggestions. Merci beaucoup.
John Doe
1
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
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 .
Veuillez expliquer exactement en quoi le codage en dur de la somme de contrôle est une bonne idée.
Scott
pas la somme de contrôle de codage en dur, sa seule clé d'identité de création de votre script, quand une autre instance s'exécutera, il vérifiera l'autre processus de script shell et cat le fichier en premier, si votre clé d'identité est sur ce fichier, donc cela signifie que votre instance est déjà en cours d'exécution.
arputra
D'ACCORD; veuillez modifier votre réponse pour l'expliquer. Et, à l'avenir, veuillez ne pas publier plusieurs longs blocs de code de 30 lignes qui semblent (presque) identiques sans dire et expliquer en quoi ils sont différents. Et ne dites pas des choses comme «vous pouvez coder en dur [sic] cksum dans votre script», et ne continuez pas à utiliser des noms de variable mysumet fsum, lorsque vous ne parlez plus de somme de contrôle.
Scott
Semble intéressant, merci! Et bienvenue sur unix.stackexchange :)
ln -s my.pid .lock
réclamera le verrou (suivi deecho $$ > my.pid
) et en cas d'échec peut vérifier si le PID stocké dans.lock
est vraiment une instance active du scriptRé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.
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.
la source
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:
la source
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 ?la source
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
la source
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 invoquerlsof <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:
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.la source
[[(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:
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écutionla source
Bien que vous ayez demandé une solution sans outils supplémentaires, c'est ma façon préférée d'utiliser
flock
:Cela vient de la section des exemples de
man flock
, qui explique plus en détail: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 .
la source
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.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.
la source
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
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 .
la source
mysum
etfsum
, lorsque vous ne parlez plus de somme de contrôle.Vous pouvez utiliser ceci: https://github.com/sayanarijit/pidlock
la source
Mon code pour toi
Basé sur
man flock
, améliorant uniquement:executing
Où je mets ici le
sleep 10
, vous pouvez mettre tout le script principal.la source