
Ich habe ein Bash-Skript, das mehrere Programme ausführt
#!/bin/sh
python program1.py &
python program2.py &
other programs ... &
lastProgram
Ich führe es als./myscript.sh
Wenn ich zum Schließen auf Ctrl+ drücke , wird es beendet und alle anderen laufen im Hintergrund weiter. Das Problem ist, dass die anderen Programme beendet werden müssen.ClastProgram
Was ist die richtige Vorgehensweise beim Schließen aller vom Skript gestarteten Programme?
Antwort1
Sammeln Sie die Prozess-IDs und beenden Sie die Hintergrundprozesse beim Beenden.
#!/bin/bash
killbg() {
for p in "${pids[@]}" ; do
kill "$p";
done
}
trap killbg EXIT
pids=()
background job 1 &
pids+=($!)
background job 2... &
pids+=($!)
foreground job
Trapping EXIT
führt die Funktion aus, wenn die Shell beendet wird, unabhängig vom Grund. Sie können das ändern, trap killbg SIGINT
um es nur auszuführen, wenn ^C
.
Dabei wird nicht überprüft, ob einer der Hintergrundprozesse beendet wurde, bevor das Skript versucht, ihn zu beenden. Wenn dies der Fall ist, können Fehler auftreten oder, schlimmer noch, der falsche Prozess beendet werden.
Oder beenden Sie sie nach Job-ID. Lesen wir die Ausgabe, jobs
um herauszufinden, welche noch aktiv sind.
#!/bin/bash
killjobs() {
for x in $(jobs | awk -F '[][]' '{print $2}' ) ; do
kill %$x
done
}
trap killjobs EXIT
sleep 999 &
sleep 1 &
sleep 999 &
sleep 30
Wenn Sie Hintergrundprozesse ausführen, die andere Prozesse starten (z. B. eine Subshell: (sleep 1234 ; echo foo) &
), müssen Sie die Jobsteuerung mit set -m
("Monitormodus") aktivieren, damit dies funktioniert. Andernfalls wird nur der Hauptprozess beendet.
Antwort2
Ich habe gerade ähnliche Fragen zum Sammeln von PIDs und zum anschließenden Löschen aller PIDs am Ende eines Skripts gelesen - dieProblemist das ein PID für einen abgeschlossenen Prozesskönnte recycelt und in einem neuen Prozess wiederverwendet werdenbevor Ihr Skript beendet ist, und dann könnten Sieeinen neuen (zufälligen) Prozess beenden.
Trap EXIT und Kill mit der Jobsteuerung von Bash
Sie können die Jobsteuerung von Bash verwenden, um nur Prozesse zu beenden, die im Skript mit einem Trap und der Jobspec „%n“ gestartet wurden, und dabei die maximale Anzahl der Jobs zu zählen, die ausgeführt werden könnten (in diesem Beispiel nur 3):
#!/bin/bash
#trap 'kill %1 %2 %3' 0 # 0 or EXIT are equivalent
#trap 'kill %1 %2 %3' EXIT # or use {1..n} as below
trap 'kill %{1..3}' EXIT
sleep 33 &
sleep 33 &
sleep 33 &
echo processes are running, ctrl-c the next sleep
sleep 66
echo this line will never be executed
Alle zusätzlichen „Kills“ an nicht vorhandenen Jobspecs, die bereits abgeschlossen sind, führen nur zu einer Fehlermeldung und beenden keine anderen neuen/zufälligen Prozesse.
Beenden Sie die gesamte Prozessgruppe Ihres Skripts
Hier ist eine etwas andere Möglichkeit, die gesamte Prozessgruppe Ihres Skripts zu beenden. Aber wenn die Jobsteuerung Ihres Skripts/Ihrer Shell nicht eingerichtet ist, dannkönnteerbt seine PPID von seinem übergeordneten Element ... aber ohne Jobkontrolle würde das oben genannte auch nicht funktionieren.
Der Unterschied besteht in diesem Kill-Befehl für Trap, der die PID von Bash verwendet, da diese zur PGID für neue Prozesse wird:
trap 'kill -- -$$' EXIT
Sehendieses verwandte Qoderhier, wo Johannes 'Fisch' Ziemke SIGINT und SIGTERM abfängt und verwendet, setsid
um "Beenden Sie die gesamte Prozessgruppe in einer neuen Prozessgruppe, damit wir nicht riskieren, uns selbst zu töten.")
Antwort3
Wenn Sie alle Hintergrundprozesse beenden möchten, wenn sie nicht vor lastProgram
Abschluss der Ausführung abgeschlossen sind, müssen Sie alle PIDs speichern:
python program1.py &
p1pid=$!
python program2.py &
p2pid=$!
other programs ... &
p3pid=$!
lastProgram
kill $p1pid $p2pid $p3pid
Wenn Sie einfach warten möchten, bis alle Hintergrundprozesse abgeschlossen sind, bevor Sie das Skript beenden, können Sie den wait
Befehl verwenden.
python program1.py &
python program2.py &
other programs ... &
lastProgram
wait
Antwort4
Hier ist mein einzeiliger Befehl (in bash
):
trap "jobs -p | xargs kill ; trap - INT" INT ; cmd1 & cmd2 & cmd3 &
# replace `cmds` with your command
Es führt ein Trapping Ctrl+ durch C, um alle Hintergrundjobs zu beenden und zum Standardverhalten zurückzukehren.
Anschließend werden alle Befehle im Hintergrund ausgeführt.
Wenn Sie Ctrl+ drücken C, werden sie alle getötet und die Falle auf ihr Standardverhalten zurückgesetzt.