@ jw013 danke! Vielleicht wird also so etwas wie ln -s my.pid .lockdie Sperre beanspruchen (gefolgt von echo $$ > my.pid) und bei einem Fehler überprüfen können, ob die in gespeicherte PID .lockwirklich eine aktive Instanz des Skripts ist
Tobias Kienzler
Antworten:
18
Fast wie nsg Antwort: Verwenden Sie ein Lock - Verzeichnis . Die Verzeichniserstellung ist unter Linux und Unix sowie unter * BSD und vielen anderen Betriebssystemen atomar.
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
Sie können die PID des Locking-Sh zu Debugging-Zwecken in eine Datei im Lock-Verzeichnis einfügen, aber nicht in die Falle tappen, dass Sie diese PID überprüfen können, um festzustellen, ob der Locking-Prozess noch ausgeführt wird. Viele Rennbedingungen liegen auf diesem Weg.
Ich würde in Betracht ziehen, die gespeicherte PID zu verwenden, um zu überprüfen, ob die Sperrinstanz noch aktiv ist. Aber hier ist eine Forderung , die mkdirnicht atomar auf NFS ist (was für mich nicht der Fall ist, aber ich denke , man sollte erwähnen, dass wenn true)
Tobias Kienzler
Ja, verwenden Sie auf jeden Fall die gespeicherte PID, um festzustellen, ob der Sperrvorgang noch ausgeführt wird, aber versuchen Sie nicht, etwas anderes als eine Nachricht zu protokollieren. Die Arbeit, die gespeicherte PID zu überprüfen, eine neue PID-Datei usw. zu erstellen, lässt ein großes Fenster für Rennen.
Bruce Ediger
Ok, wie Ihunath feststellte , wäre das lockdir höchstwahrscheinlich das, in /tmpdem normalerweise kein NFS geteilt wird, also sollte das in Ordnung sein.
Tobias Kienzler
Ich würde verwenden rm -rf, um das Sperrverzeichnis zu entfernen. rmdirschlägt fehl, wenn jemand (nicht unbedingt Sie) es geschafft hat, eine Datei zum Verzeichnis hinzuzufügen.
Chepner
18
Um Bruce Edigers Antwort zu ergänzen und sich von dieser Antwort inspirieren zu lassen , sollten Sie auch die Bereinigung intelligenter gestalten, um die Beendigung des Skripts zu verhindern:
#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
Ich habe diese Bedingung für eine Woche verwendet, und in 2 Fällen verhinderte es nicht, dass ein neuer Prozess gestartet wurde. Ich habe herausgefunden, was das Problem ist - new pid ist eine Teilzeichenfolge der alten und wird von versteckt grep -v $$. Beispiele: alt - 14532, neu - 1453, alt - 28858, neu - 858.
Naktibalda
Ich habe das grep -v $$grep -v "^${$} "
Problem
@Naktibalda guten Fang, danke! Sie können es auch mit grep -wv "^$$"(siehe Bearbeiten) beheben .
Terdon
Danke für das Update. Mein Muster schlug gelegentlich fehl, weil kürzere Pids mit Leerzeichen aufgefüllt wurden.
Naktibalda
4
Ich würde eine Sperrdatei verwenden, wie von Marco erwähnt
(Ich glaube, Sie haben vergessen, die Sperrdatei zu erstellen.) Was ist mit den Rennbedingungen ?
Tobias Kienzler
ops :) Ja, die Rennbedingungen sind in meinem Beispiel ein Problem. Normalerweise schreibe ich stündliche oder tägliche Cronjobs, und die Rennbedingungen sind selten.
nsg
Sie sollten auch in meinem Fall nicht relevant sein, aber es ist etwas, das man beachten sollte. Vielleicht ist die Verwendung auch lsof $0nicht schlecht?
Tobias Kienzler
Sie können die Wettkampfbedingungen verringern, indem Sie Ihre $$in die Sperrdatei schreiben . Dann sleepfür eine kurze Pause und lesen Sie es zurück. Wenn die PID immer noch Ihre ist, haben Sie die Sperre erfolgreich erworben. Benötigt absolut kein zusätzliches Werkzeug.
Manatwork
1
Ich habe lsof nie für diesen Zweck verwendet, ich dies sollte es funktionieren. Beachten Sie, dass lsof in meinem System sehr langsam ist (1-2 Sek.) Und wahrscheinlich viel Zeit für die Rennbedingungen zur Verfügung steht.
nsg
3
Wenn Sie sicherstellen möchten, dass nur eine Instanz Ihres Skripts ausgeführt wird, schauen Sie sich Folgendes an:
Andernfalls können Sie überprüfen psoder aufrufen lsof <full-path-of-your-script>, da ich sie nicht als zusätzliche Tools bezeichnen würde.
Ergänzung :
Eigentlich habe ich mir überlegt, es so zu machen:
for LINE in`lsof -c <your_script> -F p`;doif[ $$ -gt ${LINE#?} ] ; then
echo "'$0' is already running"1>&2
exit 1;fidone
Auf diese Weise wird sichergestellt, dass nur der Prozess mit dem niedrigsten pidWert weiter ausgeführt wird, auch wenn Sie mehrere Instanzen <your_script>gleichzeitig ausführen .
Vielen Dank für den Link, aber könnten Sie die wesentlichen Teile in Ihre Antwort aufnehmen? Es ist eine gängige Politik bei SE, um das Verrotten von Links zu verhindern ... Aber [[(lsof $0 | wc -l) > 2]] && exitvielleicht reicht so etwas tatsächlich aus, oder ist dies auch anfällig für Rennbedingungen?
Tobias Kienzler,
Sie haben Recht, der wesentliche Teil meiner Antwort fehlte, und nur Links zu posten ist ziemlich lahm. Ich habe der Antwort meinen eigenen Vorschlag hinzugefügt.
user1146332
3
Eine andere Möglichkeit, um sicherzustellen, dass eine einzelne Instanz des Bash-Skripts ausgeführt wird:
#!/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 Ruft die PID des vorhandenen Skripts ab, wenn es bereits ausgeführt wird, oder wird mit Fehlercode 1 beendet, wenn kein anderes Skript ausgeführt wird
Dies stammt aus dem Beispielteil von man flock, in dem Folgendes näher erläutert wird:
Dies ist ein nützlicher Code für Shell-Skripte. Setzen Sie es oben in das Shell-Skript, das Sie sperren möchten, und es sperrt sich automatisch beim ersten Start. Wenn env var $ FLOCKER nicht auf das ausgeführte Shell-Skript festgelegt ist, führen Sie flock aus und greifen Sie auf eine exklusive nicht blockierende Sperre zu (verwenden Sie das Skript selbst als Sperrdatei), bevor Sie sich mit den richtigen Argumenten erneut ausführen. Außerdem wird die FLOCKER-Umgebungsvariable auf den richtigen Wert gesetzt, damit sie nicht erneut ausgeführt wird.
Punkte, die man beachten sollte:
Erforderlich flock, das Beispielskript wird mit einem Fehler beendet, wenn es nicht gefunden werden kann
Dies ist eine modifizierte Version von Anselmos Antwort . Die Idee ist, einen schreibgeschützten Dateideskriptor mit dem Bash-Skript selbst zu erstellen und flockdie Sperre zu handhaben.
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"
Der Hauptunterschied zu allen anderen Antworten besteht darin, dass dieser Code das Dateisystem nicht verändert, einen sehr geringen Platzbedarf aufweist und keine Bereinigung erfordert, da der Dateideskriptor geschlossen wird, sobald das Skript unabhängig vom Beendigungsstatus beendet wird. Somit ist es egal, ob das Skript fehlschlägt oder erfolgreich ist.
Sie sollten immer alle Shell-Variablenreferenzen angeben, es sei denn, Sie haben einen guten Grund, dies nicht zu tun , und Sie sind sicher , dass Sie wissen, was Sie tun. Also solltest du machen exec 6< "$SCRIPT".
Scott
@Scott Ich habe den Code gemäß Ihren Vorschlägen geändert. Danke vielmals.
John Doe
1
Ich verwende cksum, um zu überprüfen, ob mein Skript wirklich eine einzelne Instanz ausführt, auch wenn ich den Dateinamen und den Dateipfad ändere .
Ich verwende keine Trap & Lock-Datei. Wenn mein Server plötzlich ausfällt, muss ich die gesperrte Datei manuell entfernen, nachdem der Server hochgefahren ist.
Hinweis: #! / Bin / bash in der ersten Zeile ist für grep ps erforderlich
Oder Sie können cksum in Ihrem Skript fest codieren, sodass Sie sich keine Sorgen mehr machen, wenn Sie den Dateinamen, den Pfad oder den Inhalt Ihres Skripts ändern möchten .
Bitte erläutern Sie genau, wie gut es ist, die Prüfsumme fest zu codieren.
Scott
Wenn eine andere Instanz ausgeführt wird, wird der andere Shell-Skriptprozess überprüft und die Datei zuerst kategorisiert, wenn sich Ihr Identitätsschlüssel in dieser Datei befindet. Dies bedeutet, dass Ihre Instanz bereits ausgeführt wird.
Arputra
OKAY; Bitte bearbeite deine Antwort, um das zu erklären. Und veröffentlichen Sie in Zukunft bitte nicht mehrere Codeblöcke mit einer Länge von 30 Zeilen, die aussehen, als wären sie (fast) identisch, ohne zu sagen und zu erklären, wie sie sich unterscheiden. Und sagen Sie keine Dinge wie "Sie können cksum in Ihrem Skript fest codieren", und verwenden Sie keine Variablennamen mehr mysumund fsumwenn Sie nicht mehr über eine Prüfsumme sprechen.
Scott,
Sieht interessant aus, danke! Und willkommen bei unix.stackexchange :)
ln -s my.pid .lock
die Sperre beanspruchen (gefolgt vonecho $$ > my.pid
) und bei einem Fehler überprüfen können, ob die in gespeicherte PID.lock
wirklich eine aktive Instanz des Skripts istAntworten:
Fast wie nsg Antwort: Verwenden Sie ein Lock - Verzeichnis . Die Verzeichniserstellung ist unter Linux und Unix sowie unter * BSD und vielen anderen Betriebssystemen atomar.
Sie können die PID des Locking-Sh zu Debugging-Zwecken in eine Datei im Lock-Verzeichnis einfügen, aber nicht in die Falle tappen, dass Sie diese PID überprüfen können, um festzustellen, ob der Locking-Prozess noch ausgeführt wird. Viele Rennbedingungen liegen auf diesem Weg.
quelle
mkdir
nicht atomar auf NFS ist (was für mich nicht der Fall ist, aber ich denke , man sollte erwähnen, dass wenn true)/tmp
dem normalerweise kein NFS geteilt wird, also sollte das in Ordnung sein.rm -rf
, um das Sperrverzeichnis zu entfernen.rmdir
schlägt fehl, wenn jemand (nicht unbedingt Sie) es geschafft hat, eine Datei zum Verzeichnis hinzuzufügen.Um Bruce Edigers Antwort zu ergänzen und sich von dieser Antwort inspirieren zu lassen , sollten Sie auch die Bereinigung intelligenter gestalten, um die Beendigung des Skripts zu verhindern:
quelle
if ! mkdir "$LOCKDIR"; then handle failure to lock and exit; fi trap and do processing after if-statement
.Dies kann zu einfach sein, bitte korrigieren Sie mich, wenn ich falsch liege. Ist das nicht einfach
ps
genug?quelle
grep -v $$
. Beispiele: alt - 14532, neu - 1453, alt - 28858, neu - 858.grep -v $$
grep -v "^${$} "
grep -wv "^$$"
(siehe Bearbeiten) beheben .Ich würde eine Sperrdatei verwenden, wie von Marco erwähnt
quelle
lsof $0
nicht schlecht?$$
in die Sperrdatei schreiben . Dannsleep
für eine kurze Pause und lesen Sie es zurück. Wenn die PID immer noch Ihre ist, haben Sie die Sperre erfolgreich erworben. Benötigt absolut kein zusätzliches Werkzeug.Wenn Sie sicherstellen möchten, dass nur eine Instanz Ihres Skripts ausgeführt wird, schauen Sie sich Folgendes an:
Sperren Sie Ihr Skript (gegen parallele Ausführung)
Andernfalls können Sie überprüfen
ps
oder aufrufenlsof <full-path-of-your-script>
, da ich sie nicht als zusätzliche Tools bezeichnen würde.Ergänzung :
Eigentlich habe ich mir überlegt, es so zu machen:
Auf diese Weise wird sichergestellt, dass nur der Prozess mit dem niedrigsten
pid
Wert weiter ausgeführt wird, auch wenn Sie mehrere Instanzen<your_script>
gleichzeitig ausführen .quelle
[[(lsof $0 | wc -l) > 2]] && exit
vielleicht reicht so etwas tatsächlich aus, oder ist dies auch anfällig für Rennbedingungen?Eine andere Möglichkeit, um sicherzustellen, dass eine einzelne Instanz des Bash-Skripts ausgeführt wird:
pidof -o %PPID -x $0
Ruft die PID des vorhandenen Skripts ab, wenn es bereits ausgeführt wird, oder wird mit Fehlercode 1 beendet, wenn kein anderes Skript ausgeführt wirdquelle
Obwohl Sie nach einer Lösung ohne zusätzliche Tools gefragt haben, verwende ich am liebsten die folgenden Methoden
flock
:Dies stammt aus dem Beispielteil von
man flock
, in dem Folgendes näher erläutert wird:Punkte, die man beachten sollte:
flock
, das Beispielskript wird mit einem Fehler beendet, wenn es nicht gefunden werden kannSiehe auch https://stackoverflow.com/questions/185451/quick-and-dirty-way-to-sure-only-one-instance-of-a-shell-script-is-running-at .
quelle
Dies ist eine modifizierte Version von Anselmos Antwort . Die Idee ist, einen schreibgeschützten Dateideskriptor mit dem Bash-Skript selbst zu erstellen und
flock
die Sperre zu handhaben.Der Hauptunterschied zu allen anderen Antworten besteht darin, dass dieser Code das Dateisystem nicht verändert, einen sehr geringen Platzbedarf aufweist und keine Bereinigung erfordert, da der Dateideskriptor geschlossen wird, sobald das Skript unabhängig vom Beendigungsstatus beendet wird. Somit ist es egal, ob das Skript fehlschlägt oder erfolgreich ist.
quelle
exec 6< "$SCRIPT"
.Ich verwende cksum, um zu überprüfen, ob mein Skript wirklich eine einzelne Instanz ausführt, auch wenn ich den Dateinamen und den Dateipfad ändere .
Ich verwende keine Trap & Lock-Datei. Wenn mein Server plötzlich ausfällt, muss ich die gesperrte Datei manuell entfernen, nachdem der Server hochgefahren ist.
Hinweis: #! / Bin / bash in der ersten Zeile ist für grep ps erforderlich
Oder Sie können cksum in Ihrem Skript fest codieren, sodass Sie sich keine Sorgen mehr machen, wenn Sie den Dateinamen, den Pfad oder den Inhalt Ihres Skripts ändern möchten .
quelle
mysum
undfsum
wenn Sie nicht mehr über eine Prüfsumme sprechen.Sie können dies verwenden: https://github.com/sayanarijit/pidlock
quelle
Mein Code für Sie
Basierend auf
man flock
, nur verbessern:executing
Wo ich das hier hinstelle
sleep 10
, kann man das ganze Hauptskript setzen.quelle