Bagaimana cara mendapatkan ID proses dari proses latar belakang?

375

Saya memulai proses latar belakang dari skrip shell saya, dan saya ingin menghentikan proses ini ketika skrip saya selesai.

Bagaimana cara mendapatkan PID dari proses ini dari skrip shell saya? Sejauh yang saya bisa lihat variabel $!berisi PID dari skrip saat ini, bukan proses latar belakang.

Volodymyr Bezuglyy
sumber
8
$! benar. Anda yakin memulai skrip dalam BG? tolong sampel.
pixelbeat
7
Ya $! benar. Saya salah.
Volodymyr Bezuglyy
11
$$ berisi skrip PID saat ini.
HUB

Jawaban:

583

Anda harus menyimpan PID dari proses latar belakang saat Anda memulainya:

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

Anda tidak dapat menggunakan kontrol pekerjaan, karena itu adalah fitur interaktif dan terikat ke terminal pengendali. Sebuah skrip tidak harus memiliki terminal yang terpasang sama sekali sehingga kontrol pekerjaan tidak harus tersedia.

camh
sumber
22
Sejak $! mengembalikan pid proses latar belakang terakhir. Mungkinkah ada sesuatu yang dimulai di antara foodan $!, dan kita mendapatkan pid sesuatu itu bukannya dari fooitu?
WiSaGaN
53
@WiSaGaN: Tidak. Tidak ada yang tersirat di antara kalimat-kalimat itu. Aktivitas lain apa pun pada sistem tidak akan memengaruhi ini. $! akan memperluas ke PID dari proses latar belakang terakhir di shell itu .
camh
8
... yang menyiram Anda jika fookebetulan ada beberapa perintah pipa (mis. tail -f somefile.txt | grep sometext). Dalam kasus seperti itu, Anda akan mendapatkan PID dari perintah grep $!daripada dari perintah tail jika itu yang Anda cari. Anda harus menggunakan jobsatau pssuka dalam hal ini.
John Rix
1
@ JohnRix: Tidak harus. Anda akan mendapatkan pid dari grep, tetapi jika Anda membunuh itu, ekor akan mendapatkan SIGPIPE ketika mencoba untuk menulis ke pipa. Tetapi begitu Anda mencoba masuk ke proses manajemen / kontrol yang rumit, bash / shell menjadi sangat menyakitkan.
camh
5
Solusi lain yang layak disarankan dalam (komentar ke jawaban untuk) Bagaimana mendapatkan pid dari proses yang baru dimulai : oh, dan "oneliner": /bin/sh -c 'echo $$>/tmp/my.pid && exec program args' &- sysfault 24 Nov '10 jam 14:28
imz - Ivan Zakharyaschev
148

Anda dapat menggunakan jobs -lperintah untuk sampai ke pekerjaan tertentuL

^Z
[1]+  Stopped                 guard

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

Dalam hal ini, 46841 adalah PID.

Dari help jobs:

-L Laporkan ID grup proses dan direktori kerja pekerjaan.

jobs -p adalah pilihan lain yang hanya menunjukkan PID.

jldupont
sumber
Untuk menggunakannya dalam skrip shell, Anda harus memproses hasilnya.
Phil
6
@Phil Untuk mencantumkan daftar saja: jobs -p. Untuk daftar pid dari pekerjaan tertentu: jobs -p% 3. Tidak perlu memproses output.
Erik Aronesty
1
@Erik dengan berbagai perintah / argumen, Anda mengubah konteks komentar saya. Tanpa argumen tambahan yang disarankan output membutuhkan pemrosesan. Sarankan peningkatan jawaban!
Phil
Menyimpan PID $!setelah Anda mulai , PID lebih mudah dibawa-bawa dan lebih mudah dalam sebagian besar situasi. Itulah yang dilakukan oleh jawaban yang saat ini diterima.
tripleee
jobs -pkembali sama seperti jobs -ldi Lubuntu 16.4
Timo
46
  • $$ adalah pid skrip saat ini
  • $! adalah pid dari proses latar belakang terakhir

Berikut ini adalah transkrip sampel dari sesi bash ( %1merujuk ke nomor urut proses latar belakang seperti yang terlihat dari jobs):

$ echo $$
3748

$ sleep 100 &
[1] 192

$ echo $!
192

$ kill %1

[1]+  Terminated              sleep 100
catwalk
sumber
gema %1tidak kembali proses latar belakang pada Ubuntu saya sedangkan echo $!tidak
Timo
25

Cara yang lebih sederhana untuk membunuh semua proses anak dari skrip bash:

pkill -P $$

The -Pbendera bekerja dengan cara yang sama dengan pkilldan pgrep- itu akan proses anak, hanya dengan pkillproses anak terbunuh dan dengan pgrepPID anak dicetak ke stdout.

Alexey Polonsky
sumber
Sangat mudah! Ini adalah cara terbaik untuk memastikan Anda tidak meninggalkan proses yang terbuka di latar belakang.
lepe
@ Lepe: Tidak cukup. Jika Anda seorang kakek-nenek, ini tidak akan berhasil: setelah bash -c 'bash -c "sleep 300 &"' & berlari pgrep -P $$tidak menunjukkan apa-apa, karena tidur tidak akan menjadi anak langsung dari cangkang Anda.
petre
1
@AlexeyPolonsky: seharusnya: bunuh semua procs anak dari shell, bukan skrip. Karena $$mengacu pada shell saat ini.
Timo
tampil bash -c 'bash -c "sleep 300 &"' & ; pgrep -P $$, saya mulai stdout [1] <pid>. So at least it shows something, but this is probably not the output of pgrep`
Timo
Bahkan proses dapat memisahkannya dari proses induk. Trik sederhana adalah memanggil garpu (membuat cucu) dan kemudian biarkan anak memproses keluar sementara cucu terus melakukan pekerjaan. (daemonisasi adalah kata kunci) Tetapi bahkan jika anak terus berjalan terlalu banyak -P tidak cukup untuk menyebarkan sinyal ke cucu. Alat seperti pstree diperlukan untuk mengikuti seluruh pohon proses dependen. Tetapi ini tidak akan menangkap daemon yang dimulai dari proses karena orang tua mereka adalah proses 1. misalnya:bash -c 'bash -c "sleep 10 & wait $!"' & sleep 0.1; pstree -p $$
Pauli Nieminen
4

inilah yang telah saya lakukan. Lihatlah, semoga bisa membantu.

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

Maka jalankan saja sebagai: ./bgkill.shdengan izin yang tepat tentu saja

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
sumber
3

Anda mungkin juga dapat menggunakan pstree:

pstree -p user

Ini biasanya memberikan representasi teks dari semua proses untuk "pengguna" dan opsi -p memberikan id proses. Sejauh yang saya mengerti, tidak tergantung pada proses yang dimiliki oleh shell saat ini. Itu juga menunjukkan garpu.

villaa
sumber
1
Untuk menggunakan ini dalam skrip shell, Anda harus memproses outputnya.
Phil
1

pgrepbisa membuat Anda semua PID anak dari proses induk. Seperti disebutkan sebelumnya $$adalah PID skrip saat ini. Jadi, jika Anda ingin skrip yang membersihkan setelah itu sendiri, ini harus melakukan trik:

trap 'kill $( pgrep -P $$ | tr "\n" " " )' SIGINT SIGTERM EXIT
errant.info
sumber
Bukankah ini akan membunuh banyak?
Phil
Ya, pertanyaan tidak pernah menyebutkan menjaga beberapa anak latar belakang hidup saat keluar.
errant.info
trap 'pkill -P $$' SIGING SIGTERM EXITterlihat lebih sederhana, tetapi saya tidak mengujinya.
petre
Untuk kompatibilitas, jangan gunakan SIGawalan. Itu diizinkan oleh POSIX tetapi hanya sebagai ekstensi yang implementasi dapat mendukung: pubs.opengroup.org/onlinepubs/007904975/utilities/trap.html Dash shell misalnya tidak.
josch