Ich möchte jedoch einige verschiedene kaputte und teilweise umsetzbare Ansätze ps
und die vielen Vorbehalte, die sie haben, diskutieren , da ich immer wieder sehe, wie Leute sie verwenden.
Diese Antwort ist wirklich die Antwort auf "Warum nicht das Sperren in der Shell verwenden ps
und grep
damit umgehen?"
Unterbrochener Ansatz # 1
Zunächst ein Ansatz in einer anderen Antwort , der ein paar positive Stimmen hat, obwohl er nicht funktioniert (und niemals funktionieren könnte) und eindeutig niemals getestet wurde:
running_proc=$(ps -C bash -o pid=,cmd= | grep my_script);
if [[ "$running_proc" != "$$ bash my_script" ]]; do
echo Already locked
exit 6
fi
Korrigieren wir die Syntaxfehler und die fehlerhaften ps
Argumente und erhalten:
running_proc=$(ps -C bash -o pid,cmd | grep "$0");
echo "$running_proc"
if [[ "$running_proc" != "$$ bash $0" ]]; then
echo Already locked
exit 6
fi
Dieses Skript wird immer beendet , unabhängig davon, wie Sie es ausführen.
Wenn Sie es mit ausführen ./myscript
, ps
wird nur die Ausgabe angezeigt 12345 -bash
, die nicht mit der erforderlichen Zeichenfolge übereinstimmt 12345 bash ./myscript
, sodass dies fehlschlägt.
Wenn Sie es mit ausführen bash myscript
, werden die Dinge interessanter. Der Bash-Prozess fordert zum Ausführen der Pipeline auf, und die untergeordnete Shell führt das ps
und aus grep
. Sowohl die ursprüngliche Shell als auch die untergeordnete Shell werden in der ps
Ausgabe wie folgt angezeigt :
25793 bash myscript
25795 bash myscript
Das ist nicht die erwartete Ausgabe $$ bash $0
, daher wird Ihr Skript beendet.
Unterbrochene Annäherung # 2
Nun habe ich dem Benutzer, der den fehlerhaften Ansatz # 1 geschrieben hat, etwas Ähnliches angetan, als ich das erste Mal versuchte:
if otherpids="$(pgrep -f "$0" | grep -vFx "$$")" ; then
echo >&2 "There are other copies of the script running; exiting."
ps >&2 -fq "${otherpids//$'\n'/ }" # -q takes about a tenth the time as -p
exit 1
fi
Das funktioniert fast . Aber die Tatsache, dass man sich gabelt, um die Pfeife laufen zu lassen, wirft dies weg. Also wird dieser auch immer aussteigen.
Unzuverlässiger Ansatz # 3
pids_this_script="$(pgrep -f "$0")"
if not_this_process="$(echo "$pids_this_script" | grep -vFx "$$")"; then
echo >&2 "There are other copies of this script running; exiting."
ps -fq "${not_this_process//$'\n'/ }"
exit 1
fi
Diese Version vermeidet das Pipeline-Forking-Problem in Ansatz 2, indem zuerst alle PIDs mit dem aktuellen Skript in den Befehlszeilenargumenten abgerufen und dann diese PID-Liste separat gefiltert werden, um die PID des aktuellen Skripts wegzulassen.
Dies könnte funktionieren ... vorausgesetzt, kein anderer Prozess verfügt über eine entsprechende Befehlszeile $0
und das Skript wird immer auf die gleiche Weise aufgerufen (z. B. wenn es mit einem relativen Pfad und dann einem absoluten Pfad aufgerufen wird, bemerkt die letztere Instanz den ersteren nicht ).
Unzuverlässiger Ansatz # 4
Was ist, wenn wir die Überprüfung der vollständigen Befehlszeile überspringen, da dies möglicherweise nicht auf ein aktuell ausgeführtes Skript hinweist, und lsof
stattdessen überprüfen, ob alle Prozesse mit diesem Skript geöffnet sind?
Na ja, dieser Ansatz ist eigentlich gar nicht so schlecht:
if otherpids="$(lsof -t "$0" | grep -vFx "$$")"; then
echo >&2 "Error: There are other processes that have this script open - most likely other copies of the script running. Exiting to avoid conflicts."
ps >&2 -fq "${otherpids//$'\n'/ }"
exit 1
fi
Wenn eine Kopie des Skripts ausgeführt wird, wird die neue Instanz natürlich einwandfrei gestartet, und es werden zwei Kopien ausgeführt.
Oder wenn das ausgeführte Skript geändert wird (z. B. mit Vim oder mit a git checkout
), startet die "neue" Version des Skripts problemlos, da sowohl Vim als auch git checkout
eine neue Datei (ein neuer Inode) anstelle des altes.
Allerdings , wenn das Skript nie geändert wird und nie kopiert, dann ist diese Version ziemlich gut. Es gibt keine Racebedingung, da die Skriptdatei bereits geöffnet sein muss, bevor die Prüfung erreicht werden kann.
Es kann immer noch Fehlalarme geben, wenn die Skriptdatei in einem anderen Prozess geöffnet ist. Beachten Sie jedoch, dass vim die Skriptdatei auch dann nicht geöffnet hält, wenn sie in Vim bearbeitet werden kann, sodass keine Fehlalarme auftreten.
Denken Sie jedoch daran, dass Sie diesen Ansatz nicht verwenden sollten, wenn das Skript bearbeitet oder kopiert werden könnte, da Sie falsche Negative erhalten, dh mehrere Instanzen gleichzeitig ausgeführt werden für dich. Ich erwähne es jedoch, weil Ansatz Nr. 3 falsch positive Ergebnisse liefert (dh den Start verweigert), wenn Sie das Skript mit Vim geöffnet haben.
Also, was ist dann zu tun?
Die am häufigsten gewählte Antwort auf diese Frage liefert einen soliden Ansatz.
Vielleicht können Sie eine bessere schreiben ... aber wenn Sie nicht alle Probleme und Vorbehalte bei den oben genannten Ansätzen verstehen, ist es unwahrscheinlich, dass Sie eine Sperrmethode schreiben, die sie alle vermeidet.
kill
. und es scheint eine gute Praxis zu sein, die eigene PID in der Schlossdatei zu speichern, anstatt sie nur zu berühren.