Apa cara terbaik untuk mengirim sinyal ke semua anggota grup proses?

422

Saya ingin membunuh seluruh pohon proses. Apa cara terbaik untuk melakukan ini menggunakan bahasa scripting umum? Saya mencari solusi sederhana.

Adam Peck
sumber
3
Zombi harus pergi ketika reaper sistem berjalan. Saya akan mengakui bahwa saya telah melihat sistem di mana zombie berlama-lama, tapi itu tidak khas.
Brian Knoblauch
7
Terkadang zombie yang tersisa bertanggung jawab atas beberapa aktivitas menakutkan.
Pengguna1
Gunakan salah satu chronosatau herodesperintah.
Michael Le Barbier Grünewald
8
kill $ (pstree <PID> -p -a -l | cut -d, -f2 | cut -d '' -f1)
vrdhn
@ MichaelLeBarbierGrünewald Bisakah Anda menautkan ke program-program itu?
lalat styrofoam

Jawaban:

305

Anda tidak mengatakan jika pohon yang ingin Anda bunuh adalah kelompok proses tunggal. (Ini sering terjadi jika pohon adalah hasil forking dari awal server atau baris perintah shell.) Anda dapat menemukan grup proses menggunakan GNU ps sebagai berikut:

 ps x -o  "%p %r %y %x %c "

Jika itu adalah grup proses yang ingin Anda bunuh, cukup gunakan kill(1)perintah tetapi alih-alih berikan nomor proses, berikan negasi nomor grup. Misalnya untuk membunuh setiap proses dalam grup 5112, gunakan kill -TERM -- -5112.

Norman Ramsey
sumber
3
kill -74313 -bash: kill: 74313: spesifikasi sinyal tidak valid Jika saya menambahkan kill -15 -GPID bekerja dengan baik.
Adam Peck
51
Seperti biasa dengan hampir semua perintah, jika Anda menginginkan argumen normal yang dimulai dengan - untuk tidak diartikan sebagai saklar, awali dengan -: kill - -GPID
ysth
9
pgrepdapat menawarkan cara yang lebih mudah untuk menemukan ID grup proses. Misalnya, untuk membunuh grup proses my-script.sh, jalankan kill -TERM -$(pgrep -o my-script.sh).
Josh Kelley
9
Lebih baik lihat stackoverflow.com/questions/392022/... ini merupakan solusi yang lebih elegan dan jika Anda perlu membuat daftar anak-anak maka gunakan:ps -o pid --no-headers --ppid $PARENT_PID
Szymon Jeż
4
Dan jika Anda mengubah sedikit format dan mengurutkan, Anda bisa melihat semua proses dikelompokkan dengan baik dan dimulai dengan (berpotensi) induk grup di setiap grup:ps x -o "%r %p %y %x %c" | sort -nk1,2
haridsv
199

Bunuh semua proses milik pohon proses yang sama menggunakan ID Grup Proses ( PGID)

  • kill -- -$PGID     Gunakan sinyal default ( TERM= 15)
  • kill -9 -$PGID     Gunakan sinyal KILL(9)

Anda dapat mengambil PGIDdari ID-Proses ( PID) dari pohon proses yang sama

  • kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*')   (sinyal TERM)
  • kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*')   (sinyal KILL)

Terima kasih khusus kepada tanager dan Speakus untuk kontribusi pada $PIDruang yang tersisa dan kompatibilitas OSX.

Penjelasan

  • kill -9 -"$PGID"=> Kirim sinyal 9 ( KILL) ke semua anak dan cucu ...
  • PGID=$(ps opgid= "$PID")=> Mengambil Proses-Group-ID dari setiap proses-ID pohon, tidak hanya Proses-Parent-ID . Variasi ps opgid= $PIDadalah ps -o pgid --no-headers $PIDtempat pgiddapat diganti oleh pgrp.
    Tapi:
    • psmenyisipkan spasi terdepan saat PIDkurang dari lima digit dan disejajarkan dengan benar seperti yang diperhatikan oleh penyamakan . Kamu bisa menggunakan:
      PGID=$(ps opgid= "$PID" | tr -d ' ')
    • psdari OSX selalu mencetak header, oleh karena itu Speakus mengusulkan:
      PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"
  • grep -o [0-9]* hanya mencetak digit berturut-turut (tidak mencetak spasi atau header alfabet).

Baris perintah lebih lanjut

PGID=$(ps -o pgid= $PID | grep -o [0-9]*)
kill -TERM -"$PGID"  # kill -15
kill -INT  -"$PGID"  # correspond to [CRTL+C] from keyboard
kill -QUIT -"$PGID"  # correspond to [CRTL+\] from keyboard
kill -CONT -"$PGID"  # restart a stopped process (above signals do not kill it)
sleep 2              # wait terminate process (more time if required)
kill -KILL -"$PGID"  # kill -9 if it does not intercept signals (or buggy)

Batasan

  • Seperti yang diperhatikan oleh davide dan Hubert Kario , ketika killdiminta oleh proses yang termasuk dalam pohon yang sama, killberisiko untuk membunuh dirinya sendiri sebelum mengakhiri seluruh pembunuhan pohon.
  • Oleh karena itu, pastikan untuk menjalankan perintah menggunakan proses yang memiliki ID Proses-Grup-berbeda .

Cerita panjang

> cat run-many-processes.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./child.sh background &
./child.sh foreground
echo "ProcessID=$$ ends ($0)"

> cat child.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./grandchild.sh background &
./grandchild.sh foreground
echo "ProcessID=$$ ends ($0)"

> cat grandchild.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
sleep 9999
echo "ProcessID=$$ ends ($0)"

Jalankan pohon proses di latar belakang menggunakan '&'

> ./run-many-processes.sh &    
ProcessID=28957 begins (./run-many-processes.sh)
ProcessID=28959 begins (./child.sh)
ProcessID=28958 begins (./child.sh)
ProcessID=28960 begins (./grandchild.sh)
ProcessID=28961 begins (./grandchild.sh)
ProcessID=28962 begins (./grandchild.sh)
ProcessID=28963 begins (./grandchild.sh)

> PID=$!                    # get the Parent Process ID
> PGID=$(ps opgid= "$PID")  # get the Process Group ID

> ps fj
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
28348 28349 28349 28349 pts/3    28969 Ss   33021   0:00 -bash
28349 28957 28957 28349 pts/3    28969 S    33021   0:00  \_ /bin/sh ./run-many-processes.sh
28957 28958 28957 28349 pts/3    28969 S    33021   0:00  |   \_ /bin/sh ./child.sh background
28958 28961 28957 28349 pts/3    28969 S    33021   0:00  |   |   \_ /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3    28969 S    33021   0:00  |   |   |   \_ sleep 9999
28958 28963 28957 28349 pts/3    28969 S    33021   0:00  |   |   \_ /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3    28969 S    33021   0:00  |   |       \_ sleep 9999
28957 28959 28957 28349 pts/3    28969 S    33021   0:00  |   \_ /bin/sh ./child.sh foreground
28959 28960 28957 28349 pts/3    28969 S    33021   0:00  |       \_ /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3    28969 S    33021   0:00  |       |   \_ sleep 9999
28959 28962 28957 28349 pts/3    28969 S    33021   0:00  |       \_ /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3    28969 S    33021   0:00  |           \_ sleep 9999
28349 28969 28969 28349 pts/3    28969 R+   33021   0:00  \_ ps fj

Perintah pkill -P $PIDtidak membunuh cucu:

> pkill -P "$PID"
./run-many-processes.sh: line 4: 28958 Terminated              ./child.sh background
./run-many-processes.sh: line 4: 28959 Terminated              ./child.sh foreground
ProcessID=28957 ends (./run-many-processes.sh)
[1]+  Done                    ./run-many-processes.sh

> ps fj
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
28348 28349 28349 28349 pts/3    28987 Ss   33021   0:00 -bash
28349 28987 28987 28349 pts/3    28987 R+   33021   0:00  \_ ps fj
    1 28963 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3    28987 S    33021   0:00  \_ sleep 9999
    1 28962 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3    28987 S    33021   0:00  \_ sleep 9999
    1 28961 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3    28987 S    33021   0:00  \_ sleep 9999
    1 28960 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3    28987 S    33021   0:00  \_ sleep 9999

Perintah kill -- -$PGIDmembunuh semua proses termasuk cucu.

> kill --    -"$PGID"  # default signal is TERM (kill -15)
> kill -CONT -"$PGID"  # awake stopped processes
> kill -KILL -"$PGID"  # kill -9 to be sure

> ps fj
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
28348 28349 28349 28349 pts/3    29039 Ss   33021   0:00 -bash
28349 29039 29039 28349 pts/3    29039 R+   33021   0:00  \_ ps fj

Kesimpulan

Saya perhatikan dalam contoh ini PIDdan PGIDsama ( 28957).
Inilah mengapa saya awalnya berpikir kill -- -$PIDsudah cukup. Tapi dalam kasus proses ini menelurkan dalam Makefilesatu ID Proses ini berbeda dengan ID Grup .

Saya pikir kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)adalah trik sederhana terbaik untuk membunuh seluruh pohon proses ketika dipanggil dari ID Grup yang berbeda (pohon proses lain).

olibre
sumber
1
Hai @davide. Pertanyaan bagus. Saya pikir killharus selalu mengirim sinyal ke seluruh pohon sebelum menerima sinyal sendiri. Tetapi dalam beberapa keadaan / implementasi tertentu, killdapat mengirim sendiri sinyal, terganggu, dan kemudian menerima sinyal sendiri. Namun risikonya harus cukup minimal, dan dapat diabaikan dalam sebagian besar kasus karena bug lain harus terjadi sebelum ini. Bisakah risiko ini diabaikan dalam kasus Anda? Selain itu jawaban lain memiliki bug umum ini ( killbagian dari pohon proses dibunuh). Semoga ini bisa membantu .. Ceria;)
olibre
3
Ini hanya berfungsi jika sub perintah sendiri tidak menjadi pemimpin kelompok. Bahkan alat sederhana seperti manitu suka melakukannya. Di sisi lain, jika Anda ingin mematikan proses gandchild dari proses anak, kill -- -$pidtidak akan berhasil. Jadi itu bukan solusi umum.
Hubert Kario
1
Contohnya adalah "anak" yang mencoba membunuh anak-anaknya (jadi cucu dari perintah yang diinisiasi pengguna). TKI, cobalah untuk mematikan hierarki proses latar belakang di child.sh.
Hubert Kario
1
> kill -QUIT - "$ PGID" # sinyal yang sama dengan [CRTL + C] dari keyboard ---- QUIT harus diganti menjadi INT menjadi true
Maxim Kholyavkin
1
untuk opsi OSX --tidak ada header tidak didukung sehingga kode harus diperbarui ke: PGID = "$ (ps -o pgid" $ PID "| grep [0-9] | tr -d '')"
Maxim Kholyavkin
166
pkill -TERM -P 27888

Ini akan membunuh semua proses yang memiliki ID proses induk 27888.

Atau lebih kuat:

CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS

jadwal yang menewaskan 33 detik kemudian dan dengan sopan meminta proses untuk mengakhiri.

Lihat jawaban ini untuk mengakhiri semua keturunan.

Onlyjob
sumber
19
Dalam tes cepat saya, pgrep hanya melaporkan anak-anak langsung, jadi ini mungkin tidak membunuh seluruh hierarki.
haridsv
10
Saya setuju dengan @haridsv: pkill -Pmengirim sinyal hanya kepada anak => cucu tidak menerima sinyal => Oleh karena itu saya telah menulis jawaban lain untuk menjelaskannya. Cheers ;-)
olibre
1
Dari skrip bash, untuk membunuh anak-anak Anda sendiri, gunakan pkill -TERM -P ${$}.
François Beausoleil
3
@Onjob, bukankah berbahaya untuk tidur lalu membunuh? ID proses mungkin telah digunakan kembali oleh OS untuk saat ini: Anda mungkin membunuh proses yang bukan anak-anak Anda lagi. Saya menduga panggilan ke pkill harus dilakukan lagi untuk memastikan hal ini.
François Beausoleil
2
@Onlyjob FYI, saya dapat menelurkan 32768 proses, berulir tunggal, dengan akses I / O ringan dalam waktu kurang dari 19 detik pada mesin saya:$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
cychoi
102

Untuk membunuh pohon proses secara rekursif, gunakan killtree ():

#!/bin/bash

killtree() {
    local _pid=$1
    local _sig=${2:--TERM}
    kill -stop ${_pid} # needed to stop quickly forking parent from producing children between child killing and parent killing
    for _child in $(ps -o pid --no-headers --ppid ${_pid}); do
        killtree ${_child} ${_sig}
    done
    kill -${_sig} ${_pid}
}

if [ $# -eq 0 -o $# -gt 2 ]; then
    echo "Usage: $(basename $0) <pid> [signal]"
    exit 1
fi

killtree $@
zhigang
sumber
3
The --argumen untuk pstidak bekerja pada OS X. Untuk membuatnya bekerja ada mengganti psperintah dengan: ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/gdi mana _regexdidefinisikan sebelum forlingkaran:local _regex="[ ]*([0-9]+)[ ]+${_pid}"
artur
5
Proses berhenti tidak terbunuh dengan SIGTERM. Lihat jawaban
x-yuri
1
-1 menggunakan #! / Bin / bash alih-alih #! / Usr / bin / env bash (atau lebih baik POSIX hanya membangun dan / bin / sh)
Orang Baik
Apakah cukup mengirim SIGKILL sebagai pengganti SIGTERM untuk menjamin bahwa itu killtree()berfungsi dengan andal?
Davide
3
jika pstidak mendukung --ppid, seseorang dapat menggunakan pgrep -P ${_pid}sebagai gantinya
Hubert Kario
16

perintah rkill daripaket pslist mengirimkan sinyal yang diberikan (atauSIGTERMsecara default) ke proses yang ditentukan dan semua turunannya:

rkill [-SIG] pid/name...
Onlyjob
sumber
11

jawaban brad adalah apa yang saya rekomendasikan juga, kecuali bahwa Anda dapat menghapus awksemuanya jika Anda menggunakan --ppidopsi tersebut ps.

for child in $(ps -o pid -ax --ppid $PPID) do ....... done
Kim Stebel
sumber
Ini tidak berfungsi untuk saya kecuali saya menghapus -ax, untuk beberapa alasan (Centos5). Kalau tidak, ini bagus!
xitrium
9

jika Anda tahu lulus pid dari proses induk, inilah skrip shell yang seharusnya berfungsi:

for child in $(ps -o pid,ppid -ax | \
   awk "{ if ( \$2 == $pid ) { print \$1 }}")
do
  echo "Killing child process $child because ppid = $pid"
  kill $child
done
brad.lane
sumber
Beberapa versi ps akan memberikan peringatan jika Anda menggunakan "-ax" alih-alih "ax". Jadi: untuk anak dalam $ (ps -o pid, ppid ax | \ awk "{if (\ $ 2 == $ pid) {print \ $ 1}}")
Cory R. King
9

Saya menggunakan versi yang sedikit dimodifikasi dari metode yang dijelaskan di sini: https://stackoverflow.com/a/5311362/563175

Jadi terlihat seperti itu:

kill `pstree -p 24901 | sed 's/(/\n(/g' | grep '(' | sed 's/(\(.*\)).*/\1/' | tr "\n" " "`

di mana 24901 adalah PID induk.

Itu terlihat sangat jelek tetapi apakah itu bekerja dengan sempurna.

Tomasz Werszko
sumber
1
Menyederhanakan dengan grep, bukannya ...pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
anishsane
2
Anda harus menambahkan -luntuk pstree, sehingga antrean panjang tidak mendapatkan dipotong; dapat dibuat lebih mudah untuk dibaca juga dengan kill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`(tidak perlu mengkonversi \nke ruang karena akan berfungsi dengan baik), thx!
Aquarius Power
9

Versi modifikasi dari jawaban zhigang:

#!/usr/bin/env bash
set -eu

killtree() {
    local pid
    for pid; do
        kill -stop $pid
        local cpid
        for cpid in $(pgrep -P $pid); do
            killtree $cpid
        done
        kill $pid
        kill -cont $pid
        wait $pid 2>/dev/null || true
   done
}

cpids() {
    local pid=$1 options=${2:-} space=${3:-}
    local cpid
    for cpid in $(pgrep -P $pid); do
        echo "$space$cpid"
        if [[ "${options/a/}" != "$options" ]]; then
            cpids $cpid "$options" "$space  "
        fi
    done
}

while true; do sleep 1; done &
cpid=$!
for i in $(seq 1 2); do
    cpids $$ a
    sleep 1
done
killtree $cpid
echo ---
cpids $$ a
x-yuri
sumber
Anda wait $pidhanya dapat pada proses yang sudah Anda mulai, tidak semua precesses, jadi ini bukan solusi umum
Hubert Kario
@ Hubert Kario Dalam hal ini, tunggu hanya akan keluar dengan status non-nol dan terus menjalankan skrip. Apakah aku salah? Tetapi waitakan menekan Terminatedpesan jika itu anak-anak.
x-yuri
9

Saya tidak dapat berkomentar (tidak memiliki reputasi yang cukup), jadi saya terpaksa menambahkan jawaban baru , meskipun ini sebenarnya bukan jawaban.

Ada sedikit masalah dengan jawaban sebaliknya yang sangat bagus dan menyeluruh yang diberikan oleh @olibre pada tanggal 28 Februari. Keluaran dari ps opgid= $PIDakan berisi spasi utama untuk PID yang lebih pendek dari lima digit karena psmembenarkan kolom (rigly align the numbers). Di dalam seluruh baris perintah, ini menghasilkan tanda negatif, diikuti oleh spasi, diikuti oleh grup PID. Solusi sederhana adalah menyalurkan pske truntuk menghilangkan ruang:

kill -- -$( ps opgid= $PID | tr -d ' ' )
tanager
sumber
Tanager terima kasih. Saya akan memperbaiki jawaban saya;) Cheers
olibre
8

Untuk menambahkan jawaban Norman Ramsey, mungkin ada baiknya melihat setsid jika Anda ingin membuat grup proses.
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html

Fungsi setsid () akan membuat sesi baru, jika proses panggilan bukan pemimpin grup proses. Setelah kembali proses pemanggilan akan menjadi ketua sesi sesi baru ini, akan menjadi ketua kelompok proses dari kelompok proses baru, dan tidak akan memiliki terminal pengendali. ID grup proses dari proses pemanggilan harus sama dengan ID proses dari proses pemanggilan. Proses panggilan akan menjadi satu-satunya proses dalam kelompok proses baru dan satu-satunya proses dalam sesi baru.

Yang saya maksud adalah bahwa Anda dapat membuat grup dari proses awal. Saya menggunakan ini dalam php untuk dapat membunuh seluruh pohon proses setelah memulainya.

Ini mungkin ide yang buruk. Saya tertarik dengan komentar.

hajamie
sumber
Sebenarnya ini adalah ide bagus dan bekerja dengan sangat baik. Saya menggunakannya dalam kasus di mana saya dapat menempatkan proses dalam kelompok proses yang sama (atau mereka semua sudah dalam kelompok yang sama).
sickill
7

Terinspirasi oleh komentar ysth

kill -- -PGID

alih-alih memberikan nomor proses, berikan negasi nomor grup. Seperti biasa dengan hampir semua perintah, jika Anda menginginkan argumen normal yang dimulai dengan a -untuk tidak diartikan sebagai switch, awali dengan--

Steven Penny
sumber
Ups, saya baru sadar bahwa saya telah memberikan jawaban yang sama seperti Anda => +1. Tapi apalagi saya menjelaskan bagaimana untuk hanya mendapatkan PGIDdari PID. Bagaimana menurut anda? Cheers
olibre
5

Fungsi shell berikut mirip dengan banyak jawaban lain, tetapi berfungsi baik di Linux dan BSD (OS X, dll) tanpa dependensi eksternal seperti pgrep:

killtree() {
    local parent=$1 child
    for child in $(ps -o ppid= -o pid= | awk "\$1==$parent {print \$2}"); do
        killtree $child
    done
    kill $parent
}
David Röthlisberger
sumber
Saya pikir Anda memiliki kata "anak" tambahan di akhir baris ke-2.
mato
@ mato - Itu tidak ekstra. Ini membatasi ruang lingkup $childuntuk fungsi itu agar tidak mengganggu variabel lain (non-lokal) dengan nama yang sama dan untuk memastikan nilai variabel lokal dibersihkan setelah fungsi berakhir.
Adam Katz
Oh, saya mengerti sekarang, saya pikir itu adalah bagian dari tugas. Saya kira saya harus membaca kembali (lebih lambat) sebelum menulis komentar. :) Terima kasih.
mato
Btw, bukankah lebih baik membuat daftar anak-anak dan kemudian mulai membunuh dari atas (orang tua)? .. Dengan demikian kita dapat menghindari situasi ketika orang tua membuat ulang anak atau terus menjalankan kode berikutnya yang berpotensi mengubah perilaku yang diinginkan.
mato
5

Sangat mudah melakukan ini dengan python menggunakan psutil . Cukup instal psutil dengan pip dan kemudian Anda memiliki serangkaian alat manipulasi proses:

def killChildren(pid):
    parent = psutil.Process(pid)
    for child in parent.get_children(True):
        if child.is_running():
            child.terminate()
generik
sumber
5

Berdasarkan jawaban zhigang, ini menghindari bunuh diri:

init_killtree() {
    local pid=$1 child

    for child in $(pgrep -P $pid); do
        init_killtree $child
    done
    [ $pid -ne $$ ] && kill -kill $pid
}
davide
sumber
4

Jika Anda ingin mematikan proses dengan nama:

killall -9 -g someprocessname

atau

pgrep someprocessname | xargs pkill -9 -g
Mika Vatanen
sumber
3

Ini adalah versi saya untuk membunuh semua proses anak menggunakan skrip bash. Itu tidak menggunakan rekursi dan tergantung pada perintah pgrep.

Menggunakan

killtree.sh PID SIGNAL

Isi dari killtrees.sh

#!/bin/bash
PID=$1
if [ -z $PID ];
then
    echo "No pid specified"
fi

PPLIST=$PID
CHILD_LIST=`pgrep -P $PPLIST -d,`

while [ ! -z "$CHILD_LIST" ]
do
    PPLIST="$PPLIST,$CHILD_LIST"
    CHILD_LIST=`pgrep -P $CHILD_LIST -d,`
done

SIGNAL=$2

if [ -z $SIGNAL ]
then
    SIGNAL="TERM"
fi
#do substring from comma to space
kill -$SIGNAL ${PPLIST//,/ }
marekdef
sumber
1
Ini akan gagal, jika proses baru dibuat setelah daftar anak dibuat.
ceving
3

Berikut adalah variasi jawaban @ zhigang yang tidak memiliki AWK, hanya bergantung pada kemungkinan parsing asli Bash:

function killtree {
  kill -STOP "$1"
  ps -e -o pid= -o ppid= | while read -r pid ppid
                           do
                             [[ $ppid = $1 ]] || continue
                             killtree "$pid"  || true # Skip over failures
                           done
  kill -CONT "$1"          
  kill -TERM "$1"
}

Tampaknya berfungsi dengan baik pada Mac dan Linux. Dalam situasi di mana Anda tidak dapat mengandalkan kemampuan untuk mengelola grup proses - seperti ketika menulis skrip untuk menguji perangkat lunak yang harus dibangun di beberapa lingkungan - teknik berjalan dengan pohon ini jelas sangat membantu.

solidsnack
sumber
1

Terima kasih atas kebijaksanaan Anda, kawan. Script saya membiarkan beberapa proses anak keluar dan tip negasi membuat segalanya lebih mudah. Saya menulis fungsi ini untuk digunakan dalam skrip lain jika perlu:

# kill my group's subprocesses:          killGroup
# kill also myself:                      killGroup -x
# kill another group's subprocesses:     killGroup N  
# kill that group all:                   killGroup -x N
# N: PID of the main process (= process group ID).

function killGroup () {
    local prid mainpid
    case $1 in
        -x) [ -n "$2" ] && kill -9 -$2 || kill -9 -$$ ;;
        "") mainpid=$$ ;;
         *) mainpid=$1 ;;
    esac
    prid=$(ps ax -o pid,pgid | grep $mainpid)
    prid=${prid//$mainpid/}
    kill -9 $prid 2>/dev/null
    return
}

Bersulang.


sumber
1

jika Anda memiliki pstree dan perl pada sistem Anda, Anda dapat mencoba ini:

perl -e 'kill 9, (`pstree -p PID` =~ m/\((\d+)\)/sg)'
Lyman
sumber
1

Mungkin lebih baik membunuh orang tua sebelum anak-anak; jika tidak, orang tua mungkin akan melahirkan anak baru lagi sebelum dia bunuh diri. Ini akan selamat dari pembunuhan.

Versi ps saya berbeda dari yang di atas; mungkin terlalu tua, oleh karena itu aneh ...

Menggunakan skrip shell sebagai ganti fungsi shell memiliki banyak keuntungan ...

Namun, itu pada dasarnya adalah gagasan zhigang


#!/bin/bash
if test $# -lt 1 ; then
    echo >&2 "usage: kiltree pid (sig)"
fi ;

_pid=$1
_sig=${2:-TERM}
_children=$(ps j | grep "^[ ]*${_pid} " | cut -c 7-11) ;
echo >&2 kill -${_sig} ${_pid}
kill -${_sig} ${_pid}
for _child in ${_children}; do
    killtree ${_child} ${_sig}
done
Peter Steier
sumber
perhatikan bahwa skrip @zhighang SIGSTOPs proses induk dan memberikan sinyal untuk menghentikan proses, jadi ini seharusnya tidak (AFAIK) menyebabkan kondisi balapan antara proses membuat anak-anak dan pengiriman sinyal. Versi Anda meskipun memiliki ras antara mendapatkan daftar anak-anak dan pengiriman sinyal ke orang tua.
Hubert Kario
1

Berikut ini telah diuji pada FreeBSD, Linux dan MacOS X dan hanya bergantung pada pgrep dan kill (versi ps -o tidak berfungsi di bawah BSD). Argumen pertama adalah pid orangtua yang anak-anak harus diakhiri. Argumen kedua adalah boolean untuk menentukan apakah pid orangtua harus diakhiri juga.

KillChilds() {
        local pid="${1}"
        local self="${2:-false}"

        if children="$(pgrep -P "$pid")"; then
                for child in $children; do
                        KillChilds "$child" true
                done
        fi

        if [ "$self" == true ]; then
                kill -s SIGTERM "$pid" || (sleep 10 && kill -9 "$pid" &)
        fi
}

KillChilds $$ > /dev/null 2>&1

Ini akan mengirim SIGTERM ke proses anak / cucu apa pun dalam skrip shell dan jika SIGTERM tidak berhasil, ia akan menunggu 10 detik dan kemudian mengirim kill.


Jawaban sebelumnya:

Berikut ini juga berfungsi tetapi akan membunuh shell itu sendiri di BSD.

KillSubTree() {
    local parent="${1}"
    for child in $(ps -o pid=$parent); do
            if [ $$ -ne $child ]; then (kill -s SIGTERM $child || (sleep 10 && kill -9 $child & )) > /dev/null 2>&1 ; fi
    done
}
# Example lanch from within script
KillSubTree $$ > /dev/null 2>&1
Orsiris de Jong
sumber
1

Saya mengembangkan solusi zhigang, xyuri dan solidsneck lebih lanjut:

 #!/bin/bash

if test $# -lt 1 ; then
    echo >&2 "usage: kiltree pid (sig)"
    exit 1 ;
  fi ;

_pid=$1
_sig=${2:-TERM}

# echo >&2 "killtree($_pid) mypid = $$"
# ps axwwf | grep -6 "^[ ]*$_pid " >&2 ;

function _killtree () {
    local _children
    local _child
    local _success

    if test $1 -eq $2 ; then # this is killtree - don't commit suicide!
        echo >&2 "killtree can´t kill it´s own branch - some processes will survive." ; 
        return 1 ;
      fi ;
    # this avoids that children are spawned or disappear.
    kill -SIGSTOP $2 ;

    _children=$(ps -o pid --no-headers --ppid $2) ;        
    _success=0 
    for _child in ${_children}; do
        _killtree $1 ${_child} $3 ;
        _success=$(($_success+$?)) ;
      done ;

    if test $_success -eq 0 ; then
        kill -$3 $2
      fi ;
    # when a stopped process is killed, it will linger in the system until it is continued
    kill -SIGCONT $2
    test $_success -eq 0 ;
    return $?
    }

_killtree $$ $_pid $_sig

Versi ini akan menghindari pembunuhan keturunannya - yang menyebabkan membanjirnya proses anak dalam solusi sebelumnya.

Proses dihentikan dengan benar sebelum daftar anak ditentukan, sehingga tidak ada anak baru yang dibuat atau menghilang.

Setelah terbunuh, pekerjaan yang dihentikan harus dilanjutkan untuk menghilang dari sistem.

Peter Steier
sumber
1

Pertanyaan lama, saya tahu, tetapi semua tanggapan tampaknya terus memanggil ps, yang saya tidak suka.

Solusi berbasis awk ini tidak memerlukan rekursi dan hanya memanggil ps satu kali.

awk 'BEGIN {
  p=1390
  while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2
  o=1
  while (o==1) {
    o=0
    split(p, q, " ")
    for (i in q) if (a[q[i]]!="") {
      p=p""a[q[i]]
      o=1
      a[q[i]]=""
    }
  }
  system("kill -TERM "p)
}'

Atau dalam satu baris:

awk 'BEGIN {p=1390;while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2;o=1;while (o==1) {o=0;split(p, q, " ");for (i in q) {if (a[q[i]]!="") {p=p""a[q[i]];o=1;a[q[i]]=""}}}system("kill -TERM "p)}'

Pada dasarnya idenya adalah bahwa kita membangun sebuah array (a) dari orangtua: entri anak, kemudian loop di sekitar array menemukan anak-anak untuk orang tua kita yang cocok, menambahkan mereka ke daftar orang tua kita (p) saat kita pergi.

Jika Anda tidak ingin membunuh proses level atas, maka lakukan

sub(/[0-9]*/, "", p)

tepat sebelum sistem () baris akan menghapusnya dari set kill.

Ingatlah bahwa ada kondisi balapan di sini, tapi itu benar (sejauh yang saya bisa lihat) dari semua solusi. Itu melakukan apa yang saya butuhkan karena naskah yang saya butuhkan tidak membuat banyak anak berumur pendek.

Latihan untuk pembaca adalah membuatnya menjadi loop 2-pass: setelah pass pertama, kirim SIGSTOP ke semua proses dalam daftar p, kemudian loop untuk menjalankan ps lagi dan setelah pass kedua kirim SIGTERM, lalu SIGCONT. Jika Anda tidak peduli dengan akhir yang bagus maka pass kedua hanya bisa menjadi SIGKILL, saya kira.

Whinger
sumber
0

Jika Anda tahu pid tentang hal yang ingin Anda bunuh, Anda biasanya dapat beralih dari id sesi, dan semuanya dalam sesi yang sama. Saya akan mengecek, tapi saya menggunakan ini untuk skrip yang memulai rsyncs di loop yang saya ingin mati, dan tidak memulai yang lain (karena loop) seperti yang akan terjadi jika saya baru saja membunuh semua rsync.

kill $(ps -o pid= -s $(ps -o sess --no-heading --pid 21709))

Jika Anda tidak tahu pid Anda masih bisa bersarang lagi

kill $(ps -o pid= -s $(ps -o sess --no-heading --pid $(pgrep rsync )))
Morgan
sumber
0
ps -o pid= --ppid $PPID | xargs kill -9 
Kristen
sumber
12
Jangan kill -9, sungguh.
Nathan Kidd
2
Terkadang kill -15tidak akan membantu.
Fedir RYKHTIK
0

Dalam sh perintah perintah akan daftar proses latar belakang. Dalam beberapa kasus mungkin lebih baik untuk membunuh proses terbaru terlebih dahulu, misalnya yang lebih lama membuat soket bersama. Dalam kasus tersebut, sortir PID dalam urutan terbalik. Terkadang Anda ingin menunggu saat pekerjaan menulis sesuatu pada disk atau hal-hal seperti itu sebelum berhenti.

Dan jangan bunuh jika Anda tidak perlu!

for SIGNAL in TERM KILL; do
  for CHILD in $(jobs -s|sort -r); do
    kill -s $SIGNAL $CHILD
    sleep $MOMENT
  done
done
jens
sumber
0

Membunuh proses anak dalam skrip shell:

Banyak kali kita perlu membunuh proses anak yang digantung atau diblokir karena alasan tertentu. misalnya. Masalah koneksi FTP.

Ada dua pendekatan,

1) Untuk membuat induk baru yang terpisah untuk setiap anak yang akan memantau dan membunuh proses anak setelah batas waktu tercapai.

Buat test.sh sebagai berikut,

#!/bin/bash

declare -a CMDs=("AAA" "BBB" "CCC" "DDD")
for CMD in ${CMDs[*]}; do
    (sleep 10 & PID=$!; echo "Started $CMD => $PID"; sleep 5; echo "Killing $CMD => $PID"; kill $PID; echo "$CMD Completed.") &
done
exit;

dan perhatikan proses yang memiliki nama sebagai 'uji' di terminal lain menggunakan perintah berikut.

watch -n1 'ps x -o "%p %r %c" | grep "test" '

Script di atas akan membuat 4 proses anak baru dan orang tua mereka. Setiap proses anak akan berjalan selama 10 detik. Tetapi begitu batas waktu 5dtk tercapai, proses induk masing-masing akan membunuh anak-anak itu. Jadi anak tidak akan dapat menyelesaikan eksekusi (10dtk). Bermain di sekitar waktu tersebut (beralih 10 dan 5) untuk melihat perilaku lain. Dalam hal ini anak akan menyelesaikan eksekusi dalam 5 detik sebelum mencapai batas waktu 10 detik.

2) Biarkan orang tua saat ini memantau dan membunuh proses anak setelah batas waktu tercapai. Ini tidak akan membuat orangtua terpisah untuk memantau setiap anak. Anda juga dapat mengelola semua proses anak dengan benar dalam induk yang sama.

Buat test.sh sebagai berikut,

#!/bin/bash

declare -A CPIDs;
declare -a CMDs=("AAA" "BBB" "CCC" "DDD")

CMD_TIME=15;
for CMD in ${CMDs[*]}; do
    (echo "Started..$CMD"; sleep $CMD_TIME; echo "$CMD Done";) &
    CPIDs[$!]="$RN";
    sleep 1;
done

GPID=$(ps -o pgid= $$);
CNT_TIME_OUT=10;
CNT=0;
while (true); do
    declare -A TMP_CPIDs;

    for PID in "${!CPIDs[@]}"; do
        echo "Checking "${CPIDs[$PID]}"=>"$PID;

        if ps -p $PID > /dev/null ; then
          echo "-->"${CPIDs[$PID]}"=>"$PID" is running..";
          TMP_CPIDs[$PID]=${CPIDs[$PID]};
        else
          echo "-->"${CPIDs[$PID]}"=>"$PID" is completed.";
        fi
    done

    if [ ${#TMP_CPIDs[@]} == 0 ]; then
        echo "All commands completed.";
        break;
    else
        unset CPIDs;
        declare -A CPIDs;
        for PID in "${!TMP_CPIDs[@]}"; do
            CPIDs[$PID]=${TMP_CPIDs[$PID]};
        done
        unset TMP_CPIDs;

        if [ $CNT -gt $CNT_TIME_OUT ]; then
            echo ${CPIDs[@]}"PIDs not reponding. Timeout reached $CNT sec. killing all childern with GPID $GPID..";
            kill -- -$GPID;
        fi
    fi

    CNT=$((CNT+1));
    echo "waiting since $b secs..";
    sleep 1;
done

exit;

dan perhatikan proses yang memiliki nama sebagai 'uji' di terminal lain menggunakan perintah berikut.

watch -n1 'ps x -o "%p %r %c" | grep "test" '

Script di atas akan membuat 4 proses anak baru. Kami menyimpan pids dari semua proses anak dan mengulanginya untuk memeriksa apakah prosesnya selesai atau masih berjalan. Proses anak akan dieksekusi hingga waktu CMD_TIME. Tetapi jika CNT_TIME_OUT batas waktu habis, Semua anak akan terbunuh oleh proses orang tua. Anda dapat mengganti waktu dan bermain-main dengan skrip untuk melihat perilaku. Salah satu kelemahan dari pendekatan ini adalah, ia menggunakan id grup untuk membunuh semua pohon anak. Tetapi proses induk itu sendiri milik kelompok yang sama sehingga juga akan terbunuh.

Anda mungkin perlu menetapkan id grup lain untuk proses induk jika Anda tidak ingin orangtua dibunuh.

Rincian lebih lanjut dapat ditemukan di sini,

Membunuh proses anak dalam skrip shell

Hemant Thorat
sumber
0

Script ini juga berfungsi:

#/bin/sh while true do echo "Enter parent process id [type quit for exit]" read ppid if [ $ppid -eq "quit" -o $ppid -eq "QUIT" ];then exit 0 fi for i in `ps -ef| awk '$3 == '$ppid' { print $2 }'` do echo killing $i kill $i done done

Nullpointer
sumber
0

Saya tahu itu sudah lama, tetapi itu adalah solusi yang lebih baik yang saya temukan:

killtree() { 
    for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do
        echo Terminating: $p 
        kill $p
    done
}
Luciano Andress Martini
sumber