Comment obtenir l'ID de processus du processus d'arrière-plan?

375

Je démarre un processus d'arrière-plan à partir de mon script shell, et je voudrais tuer ce processus lorsque mon script se termine.

Comment obtenir le PID de ce processus à partir de mon script shell? Autant que je puisse voir, la variable $!contient le PID du script actuel, pas le processus d'arrière-plan.

Volodymyr Bezuglyy
la source
8
$! est correct. Êtes-vous sûr de commencer le script dans le BG? échantillon s'il vous plaît.
pixelbeat
7
Oui $! est correct. J'avais tort.
Volodymyr Bezuglyy
11
$$ contient le PID de script actuel.
HUB

Réponses:

583

Vous devez enregistrer le PID du processus d'arrière-plan au moment où vous le démarrez:

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

Vous ne pouvez pas utiliser le contrôle des travaux, car il s'agit d'une fonction interactive et liée à un terminal de contrôle. Un script n'aura pas nécessairement un terminal attaché du tout, donc le contrôle des travaux ne sera pas nécessairement disponible.

camh
la source
22
Depuis $! renvoie le dernier pid du processus d'arrière-plan. Est-il possible que quelque chose commence entre fooet $!, et nous obtenons le pid de quelque chose au lieu de celui foode?
WiSaGaN
53
@WiSaGaN: Non. Il n'y a rien entre ces lignes. Toute autre activité sur le système n'affectera pas cela. $! se développera jusqu'au PID du dernier processus d'arrière-plan dans ce shell .
camh
8
... qui vous arrose s'il foose trouve être plusieurs commandes piped (par exemple. tail -f somefile.txt | grep sometext). Dans de tels cas, vous obtiendrez le PID de la commande grep $!plutôt que la commande tail si c'est ce que vous cherchiez. Vous devrez utiliser jobsou psou les likes dans ce cas.
John Rix
1
@JohnRix: Pas nécessairement. Vous obtiendrez le pid de grep, mais si vous le tuez, tail recevra un SIGPIPE quand il essaiera d'écrire dans le tuyau. Mais dès que vous essayez d'entrer dans une gestion / contrôle de processus délicat, bash / shell devient assez douloureux.
camh
5
Une autre solution valable est suggérée dans (un commentaire à une réponse à) Comment obtenir le pid du processus qui vient de commencer : oh, et le "oneliner": /bin/sh -c 'echo $$>/tmp/my.pid && exec program args' &- sysfault 24 novembre 10 à 14:28
imz - Ivan Zakharyaschev
148

Vous pouvez utiliser la jobs -lcommande pour accéder à un travail particulier

^Z
[1]+  Stopped                 guard

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

Dans ce cas, 46841 est le PID.

De help jobs:

-l Signaler l'ID du groupe de processus et le répertoire de travail des travaux.

jobs -p est une autre option qui affiche uniquement les PID.

jldupont
la source
Pour l'utiliser dans un script shell, vous devez traiter la sortie.
Phil
6
@Phil Pour répertorier les pids uniquement: jobs -p. Pour lister le pid d'un certain travail: jobs -p% 3. Pas besoin de traiter la sortie.
Erik Aronesty
1
@Erik avec différentes commandes / arguments vous changez le contexte de mon commentaire. Sans l'argument supplémentaire suggéré, la sortie nécessite un traitement. Suggérez une amélioration à la réponse!
Phil
L'enregistrement du PID $!juste après avoir commencé est plus portable et plus simple dans la plupart des situations. C'est ce que fait la réponse actuellement acceptée.
tripleee
jobs -pretourné le même que jobs -lsur Lubuntu 16.4
Timo
46
  • $$ est le pid du script actuel
  • $! est le pid du dernier processus d'arrière-plan

Voici un exemple de transcription d'une session bash (se %1réfère au nombre ordinal de processus d'arrière-plan vu de jobs):

$ echo $$
3748

$ sleep 100 &
[1] 192

$ echo $!
192

$ kill %1

[1]+  Terminated              sleep 100
passerelle
la source
echo %1ne renvoie pas le processus d'arrière-plan sur mon Ubuntu alors echo $!que
Timo
25

Un moyen encore plus simple de tuer tous les processus enfants d'un script bash:

pkill -P $$

Le -Pdrapeau fonctionne de la même manière avec pkillet pgrep- il obtient les processus enfants, seulement avec pkillles processus enfants qui sont tués et avec pgreples PID enfants sont imprimés sur stdout.

Alexey Polonsky
la source
Très pratique! C'est le meilleur moyen de vous assurer de ne pas laisser les processus ouverts en arrière-plan.
lepe
@lepe: Pas tout à fait. Si vous êtes un grand-parent, cela ne fonctionnera pas: après avoir bash -c 'bash -c "sleep 300 &"' & couru pgrep -P $$ne montre rien, car le sommeil ne sera pas un enfant direct de votre coquille.
petre
1
@AlexeyPolonsky: ça devrait être: tuer tous les procs enfants d'un shell, pas un script. Parce que $$fait référence au shell actuel.
Timo
performant bash -c 'bash -c "sleep 300 &"' & ; pgrep -P $$, je [1] <pid>. So at least it shows something, but this is probably not the output of monte sur stdout pgrep`
Timo
Même les processus peuvent les détacher du processus parent. L'astuce simple consiste à appeler fork (créer un petit-enfant), puis à laisser le processus enfant se terminer pendant que le petit-enfant continue de faire le travail. (la démonisation est le mot clé) Mais même si l'enfant continue à fonctionner trop, pkill -P n'est pas suffisant pour propager le signal aux petits-enfants. Un outil comme pstree est requis pour suivre tout l'arbre de processus dépendant. Mais cela n'attrapera pas les démons démarrés à partir du processus car leur parent est le processus 1. Par exemple:bash -c 'bash -c "sleep 10 & wait $!"' & sleep 0.1; pstree -p $$
Pauli Nieminen
4

c'est ce que j'ai fait. Vérifiez-le, j'espère que cela peut aider.

#!/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

Ensuite, exécutez-le comme: ./bgkill.shavec les autorisations appropriées bien sûr

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
la source
3

Vous pouvez également utiliser pstree:

pstree -p user

Cela donne généralement une représentation textuelle de tous les processus pour "l'utilisateur" et l'option -p donne l'ID de processus. Pour autant que je sache, cela ne dépend pas de la propriété des processus par le shell actuel. Il montre également des fourches.

villaa
la source
1
Pour l'utiliser dans un script shell, vous devez traiter fortement la sortie.
Phil
1

pgreppeut vous obtenir tous les PID enfants d'un processus parent. Comme mentionné précédemment, $$le PID des scripts actuels. Donc, si vous voulez un script qui se nettoie après lui-même, cela devrait faire l'affaire:

trap 'kill $( pgrep -P $$ | tr "\n" " " )' SIGINT SIGTERM EXIT
errant.info
la source
Cela ne tuerait-il pas le sort?
Phil
Oui, la question n'a jamais mentionné le maintien en vie de certains enfants de fond à la sortie.
errant.info
trap 'pkill -P $$' SIGING SIGTERM EXITsemble plus simple, mais je ne l'ai pas testé.
petre
Pour des raisons de compatibilité, n'utilisez pas le SIGpréfixe. Il est autorisé par POSIX mais juste comme une extension que les implémentations peuvent prendre en charge: pubs.opengroup.org/onlinepubs/007904975/utilities/trap.html Le shell de tirets par exemple ne le fait pas.
josch