Wie erhalte ich die Prozess-ID des Hintergrundprozesses?

375

Ich starte einen Hintergrundprozess von meinem Shell-Skript aus und möchte diesen Prozess beenden, wenn mein Skript beendet ist.

Wie erhalte ich die PID dieses Prozesses aus meinem Shell-Skript? Soweit ich sehen kann, $!enthält die Variable die PID des aktuellen Skripts, nicht den Hintergrundprozess.

Volodymyr Bezuglyy
quelle
8
$! ist richtig. Sind Sie sicher, dass Sie das Skript im Hintergrund starten? Probe bitte.
Pixelbeat
7
Ja $! ist richtig. Ich lag falsch.
Volodymyr Bezuglyy
11
$$ enthält die aktuelle Skript-PID.
Hub

Antworten:

583

Sie müssen die PID des Hintergrundprozesses zum Zeitpunkt des Starts speichern:

foo &
FOO_PID=$!
# do other stuff
kill $FOO_PID

Sie können die Jobsteuerung nicht verwenden, da dies eine interaktive Funktion ist und an ein steuerndes Terminal gebunden ist. An ein Skript muss nicht unbedingt ein Terminal angeschlossen sein, sodass die Jobsteuerung nicht unbedingt verfügbar ist.

camh
quelle
22
Da $! Gibt die PID des letzten Hintergrundprozesses zurück. Ist es möglich, dass etwas zwischen foound beginnt $!und wir bekommen, dass etwas pid statt foo's ist?
WiSaGaN
53
@ WiSaGaN: Nein. Zwischen diesen Zeilen steht nichts. Andere Aktivitäten auf dem System haben keine Auswirkungen darauf. $! wird auf die PID des letzten Hintergrundprozesses in dieser Shell erweitert .
Camh
8
... was Sie abspritzt, wenn fooes sich um Befehle mit mehreren Leitungen handelt (z. B. tail -f somefile.txt | grep sometext). In solchen Fällen erhalten Sie die PID des Befehls grep $!anstelle des Befehls tail, wenn Sie danach gesucht haben. In diesem Fall müssen Sie jobsoder psoder ähnliche verwenden.
John Rix
1
@ JohnRix: Nicht unbedingt. Sie werden die PID bekommen grep, aber wenn Sie das töten, bekommt der Schwanz ein ZEICHEN, wenn er versucht, in die Pipe zu schreiben. Sobald Sie jedoch versuchen, in eine schwierige Prozessverwaltung / -steuerung einzusteigen, wird Bash / Shell ziemlich schmerzhaft.
Camh
5
Eine andere würdige Lösung wird vorgeschlagen in (ein Kommentar zu einer Antwort auf) Wie man von dem gerade gestarteten Prozess pid bekommt : Oh, und der "Oneliner": /bin/sh -c 'echo $$>/tmp/my.pid && exec program args' &- Sysfault 24. November 10 um 14:28
imz - Ivan Zakharyaschev
148

Mit dem jobs -lBefehl können Sie zu einem bestimmten Job gelangen

^Z
[1]+  Stopped                 guard

my_mac:workspace r$ jobs -l
[1]+ 46841 Suspended: 18           guard

In diesem Fall ist 46841 die PID.

Von help jobs:

-l Geben Sie die Prozessgruppen-ID und das Arbeitsverzeichnis der Jobs an.

jobs -p ist eine weitere Option, die nur die PIDs anzeigt.

jldupont
quelle
Um dies in einem Shell-Skript zu verwenden, müssten Sie die Ausgabe verarbeiten.
Phil
6
@Phil Nur Pids auflisten: jobs -p. So listen Sie die PID eines bestimmten Jobs auf: jobs -p% 3. Ausgabe muss nicht verarbeitet werden.
Erik Aronesty
1
@Erik mit verschiedenen Befehlen / Argumenten ändern Sie den Kontext meines Kommentars. Ohne das zusätzliche vorgeschlagene Argument muss die Ausgabe verarbeitet werden. Schlagen Sie eine Verbesserung der Antwort vor!
Phil
Das Speichern der PID $!unmittelbar nach dem Start ist in den meisten Situationen portabler und unkomplizierter. Das macht die aktuell akzeptierte Antwort.
Tripleee
jobs -pkehrte das gleiche zurück wie jobs -lauf Lubuntu 16.4
Timo
46
  • $$ ist die PID des aktuellen Skripts
  • $! ist die PID des letzten Hintergrundprozesses

Hier ist ein Beispielprotokoll einer Bash-Sitzung ( %1bezieht sich auf die Ordnungszahl des Hintergrundprozesses, wie aus ersichtlich jobs):

$ echo $$
3748

$ sleep 100 &
[1] 192

$ echo $!
192

$ kill %1

[1]+  Terminated              sleep 100
Laufsteg
quelle
Echo %1Rückkehr auf meinem Ubuntu den Hintergrundprozess nicht während echo $!tut
Timo
25

Eine noch einfachere Möglichkeit, den gesamten untergeordneten Prozess eines Bash-Skripts zu beenden:

pkill -P $$

Das -PFlag funktioniert genauso mit pkillund pgrep- es erhält untergeordnete Prozesse, nur wenn die untergeordneten pkillProzesse beendet werden und mit pgrepuntergeordneten PIDs auf stdout gedruckt werden.

Alexey Polonsky
quelle
Sehr angenehm! Dies ist der beste Weg, um sicherzustellen, dass geöffnete Prozesse nicht im Hintergrund bleiben.
Lepe
@lepe: Nicht ganz. Wenn Sie Großeltern sind, funktioniert dies nicht: Nach dem bash -c 'bash -c "sleep 300 &"' & Laufen pgrep -P $$wird nichts angezeigt, da der Schlaf kein direktes Kind Ihrer Muschel ist.
Petre
1
@AlexeyPolonsky: Es sollte sein: alle untergeordneten Prozesse einer Shell töten, kein Skript. Weil $$bezieht sich auf die aktuelle Shell.
Timo
Aufführen bash -c 'bash -c "sleep 300 &"' & ; pgrep -P $$, ich komme auf stdout [1] <pid>. So at least it shows something, but this is probably not the output of pgrep`
Timo
Sogar Prozesse können sie vom übergeordneten Prozess trennen. Ein einfacher Trick besteht darin, Fork aufzurufen (ein Enkelkind zu erstellen) und dann den untergeordneten Prozess beenden zu lassen, während das Enkelkind die Arbeit fortsetzt. (Daemonisierung ist das Schlüsselwort) Aber selbst wenn das Kind weiter zu pkill-P läuft, reicht es nicht aus, um das Signal an Enkelkinder weiterzugeben. Ein Werkzeug wie pstree ist erforderlich, um dem gesamten abhängigen Prozessbaum zu folgen. Aber dies wird keine Dämonen fangen, die aus dem Prozess gestartet wurden, da ihre Eltern Prozess 1 sind. Beispiel:bash -c 'bash -c "sleep 10 & wait $!"' & sleep 0.1; pstree -p $$
Pauli Nieminen
4

das habe ich getan Probieren Sie es aus, hoffe es kann helfen.

#!/bin/bash
#
# So something to show.
echo "UNO" >  UNO.txt
echo "DOS" >  DOS.txt
#
# Initialize Pid List
dPidLst=""
#
# Generate background processes
tail -f UNO.txt&
dPidLst="$dPidLst $!"
tail -f DOS.txt&
dPidLst="$dPidLst $!"
#
# Report process IDs
echo PID=$$
echo dPidLst=$dPidLst
#
# Show process on current shell
ps -f
#
# Start killing background processes from list
for dPid in $dPidLst
do
        echo killing $dPid. Process is still there.
        ps | grep $dPid
        kill $dPid
        ps | grep $dPid
        echo Just ran "'"ps"'" command, $dPid must not show again.
done

Dann führen Sie es einfach aus als: ./bgkill.shnatürlich mit den richtigen Berechtigungen

root@umsstd22 [P]:~# ./bgkill.sh
PID=23757
dPidLst= 23758 23759
UNO
DOS
UID        PID  PPID  C STIME TTY          TIME CMD
root      3937  3935  0 11:07 pts/5    00:00:00 -bash
root     23757  3937  0 11:55 pts/5    00:00:00 /bin/bash ./bgkill.sh
root     23758 23757  0 11:55 pts/5    00:00:00 tail -f UNO.txt
root     23759 23757  0 11:55 pts/5    00:00:00 tail -f DOS.txt
root     23760 23757  0 11:55 pts/5    00:00:00 ps -f
killing 23758. Process is still there.
23758 pts/5    00:00:00 tail
./bgkill.sh: line 24: 23758 Terminated              tail -f UNO.txt
Just ran 'ps' command, 23758 must not show again.
killing 23759. Process is still there.
23759 pts/5    00:00:00 tail
./bgkill.sh: line 24: 23759 Terminated              tail -f DOS.txt
Just ran 'ps' command, 23759 must not show again.
root@umsstd22 [P]:~# ps -f
UID        PID  PPID  C STIME TTY          TIME CMD
root      3937  3935  0 11:07 pts/5    00:00:00 -bash
root     24200  3937  0 11:56 pts/5    00:00:00 ps -f
Luis Ramirez
quelle
3

Möglicherweise können Sie auch pstree verwenden:

pstree -p user

Dies gibt normalerweise eine Textdarstellung aller Prozesse für den "Benutzer" und die Option -p gibt die Prozess-ID an. Soweit ich weiß, hängt es nicht davon ab, ob die Prozesse der aktuellen Shell gehören. Es zeigt auch Gabeln.

Villa
quelle
1
Um dies in einem Shell-Skript zu verwenden, müssten Sie die Ausgabe stark verarbeiten.
Phil
1

pgrepSie können alle untergeordneten PIDs eines übergeordneten Prozesses abrufen. Wie bereits erwähnt, $$ist die aktuelle Skript-PID. Wenn Sie also ein Skript möchten, das nach sich selbst bereinigt, sollte dies den Trick tun:

trap 'kill $( pgrep -P $$ | tr "\n" " " )' SIGINT SIGTERM EXIT
errant.info
quelle
Würde das nicht das Los töten?
Phil
Ja, die Frage erwähnte nie, dass einige Hintergrundkinder beim Verlassen am Leben bleiben sollten.
errant.info
trap 'pkill -P $$' SIGING SIGTERM EXITsieht einfacher aus, aber ich habe es nicht getestet.
Petre
Verwenden Sie aus Kompatibilitätsgründen nicht das SIGPräfix. Es ist von POSIX zulässig, jedoch nur als Erweiterung, die Implementierungen möglicherweise unterstützen: pubs.opengroup.org/onlinepubs/007904975/utilities/trap.html Die Dash-Shell beispielsweise nicht.
Josch