Starten Sie Hintergrundprozesse in einer Gruppe und beenden Sie sie später alle

Starten Sie Hintergrundprozesse in einer Gruppe und beenden Sie sie später alle

Neulich habe ich versucht, ein Skript zu schreiben, das einenPIDund alle seine untergeordneten Prozesse, aber nachdem ich einige Zeit damit verbracht hatte, entschied ich, dass es nicht vertrauenswürdig war, weil manchmal einige der untergeordneten Prozesse mit einemPPIDvon1.

Nun, was ich suche ist, wie kann ich die folgende Funktion im Hintergrund ausführen, und haben die Funktion und curlalles in der gleichenGruppe, und beenden Sie dieses Skript später, wenn es hängen bleibt, und beenden Sie dabei alle seine untergeordneten Elemente, die im Hintergrund verblieben sind:

download(){
    until curl -s -S -L -A Mozilla/5.0 -m 300 "$@"; do
        echo Retrying in 5 seconds... >&2
        sleep 5
    done
}

for url in foo.com bar.com baz.com; do
    download $url >$url &
done
wait

Ich weiß, dass diese Funktion nicht nötig ist download, aber ich habe sie hier aufgenommen, weil ich sie in diesem Skript oft verwende. Außerdem gebe ich ein maximales Zeitlimit von300zu curl, aber abhängig von einigen Netzwerkfehlern bleibt es auch hängen.

Antwort1

Wenn Sie Ihr Skript aus einer interaktiven Shell aufrufen, wird es normalerweise in eine neue Prozessgruppe (auch bekannt als job) eingefügt. Wenn Sie sich also Ctrl-Cdarin befinden, erhalten alle von diesem Skript gestarteten Prozesse ein SIGINT.

Wenn Sie es auch im Hintergrund gestartet haben (immer noch von einer interaktiven Shell aus), wird es auch in seiner eigenen Prozessgruppe gestartet.

Über Prozessgruppen können Sie sich informieren mit:

ps -j

( jdafür job controlist das Verhalten von Shells, die Befehlszeilen in Prozessgruppen ausführen, um diese verwalten zu können (Vordergrund/Hintergrund/Kill/Suspend/Fortsetzen)).

Informieren Sie sich über dieArbeitsplätzeIhrer interaktiven Shell mit dem jobsBefehl (allerdings nicht aller Prozesse darin). jobs -pzeigt Ihnen die Prozessgruppen-ID.

Sie können die Mitglieder einer Prozessgruppe beenden, indem Sie ein Signal an die Adresse senden, an -xder xsich die Prozessgruppen-ID befindet (PGID) oder durch Verwendung von Jobspezifikationen mit %job-number. Zum Beispiel:

$ sleep 30 | sleep 40 &
[1] 6950 6951
$ ps -j
  PID  PGID   SID TTY          TIME CMD
 6031  6031  6031 pts/3    00:00:00 zsh
 6950  6950  6031 pts/3    00:00:00 sleep
 6951  6950  6031 pts/3    00:00:00 sleep
 6952  6952  6031 pts/3    00:00:00 ps
$ kill -- -6950
[1]  + terminated  sleep 30 | sleep 40
$ sleep 30 | sleep 40 &
[1] 6955 6957
$ jobs
[1]  + running    sleep 30 | sleep 40
$ kill %1
[1]  + terminated  sleep 30 | sleep 40

Wenn Ihr Skript nicht von einer interaktiven Shell aus gestartet wird, landet es in derselben Prozessgruppe wie sein übergeordnetes Skript. Das Beenden dieser Prozessgruppe kann also dazu führen, dass eine Reihe anderer Prozesse beendet werden, die Sie nicht beenden möchten.

Was Sie in Ihrem Skript tun könnten, ist, die Prozessgruppe selbst zu starten.

Liken Sie, indem Sie hinzufügen:

[ "$(ps -o pgid= -p "$$")" -eq "$$" ] ||
  exec perl -e 'setpgrp or die "setpgrp; $!"; exec @ARGV' -- "$0" "$@"

perl(das das Skript nach dem Aufruf zum Starten einer neuen Prozessgruppe erneut ausführt, wenn wir feststellen, dass wir nicht Leiter einer Prozessgruppe sind) am Anfang Ihres Skripts.

Auf diese Weise wird garantiert, dass das Skript in seiner eigenen Prozessgruppe ausgeführt wird.

Dies bedeutet jedoch, dass Sie in diesem Fall Folgendes tun müssen:

something | myscript | something

in einer interaktiven Shell ist es wahrscheinlich myscriptnicht der Prozessgruppenführer. Wenn Sie das setpgrpoben genannte tun, befindet sich das Skript nicht mehr in der Vordergrundprozessgruppe des Terminals, was bedeutet, Ctrl-Cdass es nicht beendet wird.

verwandte Informationen