Come ottenere l'ID del processo in background?


375

Inizio un processo in background dal mio script di shell e vorrei terminare questo processo al termine del mio script.

Come ottenere il PID di questo processo dal mio script di shell? Per quanto posso vedere la variabile $!contiene il PID dello script corrente, non il processo in background.


8
$! è corretto. Sei sicuro di iniziare la sceneggiatura in BG? campione per favore.
pixelbeat,

7
Sì $! è corretto. Mi sbagliavo.
Volodymyr Bezuglyy,

11
$$ contiene lo script PID corrente.
HUB,

Risposte:


583

È necessario salvare il PID del processo in background al momento dell'avvio:

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

Non è possibile utilizzare il controllo dei lavori, poiché si tratta di una funzione interattiva e legata a un terminale di controllo. Uno script non avrà necessariamente un terminale collegato, quindi il controllo del lavoro non sarà necessariamente disponibile.


22
Da $! restituisce il pid dell'ultimo processo in background. È possibile che qualcosa inizi tra fooe $!, e otteniamo che qualcosa è pid invece che di foo?
WiSaGaN,

53
@WiSaGaN: No. Non c'è nulla tra quelle righe. Qualsiasi altra attività sul sistema non influirà su questo. $! si espanderà al PID dell'ultimo processo in background in quella shell .
Camh,

8
... che ti fa perdere se foocapita di essere comandi multipli (ad es. tail -f somefile.txt | grep sometext). In questi casi, otterrai il PID del comando grep $!anziché il comando tail se è quello che stavi cercando. In questo caso dovrai usare jobso pso simili.
John Rix,

1
@JohnRix: non necessariamente. Otterrai il pid di grep, ma se lo uccidi, tail riceverà un SIGPIPE quando proverà a scrivere sulla pipe. Ma non appena si tenta di entrare in qualsiasi gestione / controllo di processo complicato, bash / shell diventa abbastanza doloroso.
Camh,

5
Un'altra soluzione degna è suggerita in (un commento a una risposta a) Come ottenere un pid del processo appena iniziato : oh, e "oneliner": /bin/sh -c 'echo $$>/tmp/my.pid && exec program args' &- sysfault 24 novembre 10 alle 14:28
imz - Ivan Zakharyaschev

148

È possibile utilizzare il jobs -lcomando per accedere a un determinato lavoroL

^Z
[1]+  Stopped                 guard

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

In questo caso, 46841 è il PID.

Da help jobs:

-l Riporta l'ID del gruppo di processo e la directory di lavoro dei lavori.

jobs -p è un'altra opzione che mostra solo i PID.


Per usarlo in uno script di shell, dovresti elaborare l'output.
Phil

6
@Phil Per elencare solo i pid: jobs -p. Per elencare il pid di un determinato lavoro: jobs -p% 3. Non è necessario elaborare l'output.
Erik Aronesty,

1
@Erik con diversi comandi / argomenti cambi il contesto del mio commento. Senza l'argomento aggiuntivo suggerito, l'output richiede l'elaborazione. Suggerisci un miglioramento alla risposta!
Phil

Il salvataggio del PID da $!subito dopo l'avvio è più portatile e più semplice nella maggior parte delle situazioni. Ecco cosa fa la risposta attualmente accettata.
Tripleee,

jobs -prestituito come jobs -lsu Lubuntu 16.4
Timo

46
  • $$ è il pid dello script corrente
  • $! è il pid dell'ultimo processo in background

Ecco una trascrizione di esempio da una sessione bash (si %1riferisce al numero ordinale del processo in background visto da jobs):

$ echo $$
3748

$ sleep 100 &
[1] 192

$ echo $!
192

$ kill %1

[1]+  Terminated              sleep 100

echo %1non restituisce il processo in background sul mio Ubuntu mentre lo echo $!fa
Timo

25

Un modo ancora più semplice per uccidere tutti i processi figlio di uno script bash:

pkill -P $$

La -Pbandiera funziona allo stesso modo con pkille pgrep- ottiene processi figlio, solo con pkilli processi figlio vengono uccisi e con pgrepi PID figlio vengono stampati su stdout.


Molto conveniente! Questo è il modo migliore per assicurarsi di non lasciare processi aperti in background.
lepe,

@lepe: non proprio. Se sei un nonno, questo non funzionerà: dopo aver bash -c 'bash -c "sleep 300 &"' & eseguito pgrep -P $$non mostra nulla, perché il sonno non sarà un figlio diretto della tua shell.
petre,

1
@AlexeyPolonsky: dovrebbe essere: uccidi tutti i proc di una shell, non uno script. Perché si $$riferisce alla shell corrente.
Timo,

esibendosi bash -c 'bash -c "sleep 300 &"' & ; pgrep -P $$, [1] <pid>. So at least it shows something, but this is probably not the output of salgo su stdout pgrep`
Timo

Anche i processi possono separarli dal processo genitore. Il trucco semplice è chiamare fork (creare un nipote) e poi lasciare che il processo figlio esca mentre il nipote continua a fare il lavoro. (demonizzazione è la parola chiave) Ma anche se il bambino continua a correre troppo pkill -P non è abbastanza per propagare il segnale ai nipoti. È necessario uno strumento come pstree per seguire l'intero albero del processo dipendente. Ma questo non catturerà i demoni avviati dal processo poiché il loro genitore è il processo 1. Ad esempio:bash -c 'bash -c "sleep 10 & wait $!"' & sleep 0.1; pstree -p $$
Pauli Nieminen

4

questo è quello che ho fatto. Dai un'occhiata, spero che possa aiutarti.

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

Quindi eseguilo come: ./bgkill.shcon le autorizzazioni appropriate ovviamente

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

3

Potresti anche essere in grado di usare pstree:

pstree -p user

Questo in genere fornisce una rappresentazione testuale di tutti i processi per "l'utente" e l'opzione -p fornisce l'id-processo. Per quanto ho capito, non dipende dal fatto che i processi siano di proprietà dell'attuale shell. Mostra anche le forcelle.


1
Per usarlo in uno script di shell, dovresti elaborare pesantemente l'output.
Phil

1

pgreppuò ottenere tutti i PID figlio di un processo genitore. Come accennato in precedenza $$è l'attuale script PID. Quindi, se vuoi uno script che ripulisca dopo se stesso, questo dovrebbe fare il trucco:

trap 'kill $( pgrep -P $$ | tr "\n" " " )' SIGINT SIGTERM EXIT

Questo non ucciderebbe il lotto?
Phil

Sì, la domanda non è mai stata menzionata mantenendo in vita alcuni bambini in background all'uscita.
errant.info,

trap 'pkill -P $$' SIGING SIGTERM EXITsembra più semplice, ma non l'ho provato.
petre,

Per compatibilità, non utilizzare il SIGprefisso. È consentito da POSIX ma proprio come un'estensione che le implementazioni possono supportare: pubs.opengroup.org/onlinepubs/007904975/utilities/trap.html Ad esempio la shell dash non lo è.
josch,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.