指定したプロセスIDのプロセスとその子孫全てを再帰的にkillするシェルスクリプト
二十五日半狂乱、2日目の記事.
実行中のプロセスをkillしたいが、対象のプロセスをkillすると子プロセスがゾンビ化しちゃうからプロセスツリーを丸ごとkillしたくなった.
どうせならkillコマンドっぽくプロセスIDで指定して、指定したプロセスをrootとしてツリーの葉から根に向かってツリー上のプロセスをkillして回りたい.
車輪の再発明の匂いがプンプンするので、「そんなのこうすれば一発じゃん.」ってのがあったらご指摘いただけたらと思います.(※議論し尽くされていました)
追記:後日、↑のリンク先にある"より良いコード"について書いたので、こちらも参考をば.
コードはgithub上に上げた.
READMEにも書いたけど、目的を達成するシェル関数*1は気持ちいいくらいにシンプル.
killpstree(){ local children=`ps --ppid $1 --no-heading | awk '{ print $1 }'` for child in $children do killpstree $child done kill $1 }
プロセスツリーをkill
githubのコードは上記のコード以外は全て動作をテストするためのサンプルなので、実行すると以下のような感じで動作を確認できる.
例えば以下のような子プロセスを5つ持つプロセスを指定すると、
$ pstree -pha 5028 bash,5028 mkpstree.bash ├─sleep,5029 12345 ├─sleep,5030 12345 ├─sleep,5031 67890 ├─sleep,5032 67890 └─sleep,5033 10000
以下のような感じで...
$ bash killpstree.bash "mkpstree.bash" === start:family killing target=[5028] === bash,5028 mkpstree.bash ├─sleep,5029 12345 ├─sleep,5030 12345 ├─sleep,5031 67890 ├─sleep,5032 67890 └─sleep,5033 10000 --->>> kill 5029 --->>> kill 5030 --->>> kill 5031 --->>> kill 5032 --->>> kill 5033 --->>> kill 5028
家族皆殺し.
一家の長(pid:5028)は最後にkillされる.
psコマンドのオプション
冒頭のスクリプトでは始めに--ppidオプション*2で子プロセスをシェル変数childrenにリストアップしてしている.
上記のプロセスツリーで言えばこんな感じ.
$ ps --ppid 5028 PID TTY TIME CMD 5029 pts/0 00:00:00 sleep 5030 pts/0 00:00:00 sleep 5031 pts/0 00:00:00 sleep 5032 pts/0 00:00:00 sleep 5033 pts/0 00:00:00 sleep
ここから左端のプロセスIDだけを抽出している.
日頃からプロセス群と戦っている人にとっては当たり前かもしれないけれど、psコマンドはオプションを工夫すれば実に賢く働いてくれることがわかった.
例えばpsの出力をプロセスツリー状にしたいなら、
$ ps aux --forest
$ ps axjf
出力フォーマットを指定したりもできる.
$ ps -o user,group,pid,ppid,gid,sid
とまぁこんな具合のサンプルがman psを叩けば冒頭にたくさん出てくるので一度サンプルだけでも目を通しておくと捗るかも.
一応バージョンを明記.
$ ps --version
procps-ng version 3.3.3
sleepのゾンビプロセスだらけになったら
遊んでいると操作に失敗してsleepのゾンビだらけになるので、目障りになったら以下でまとめてkill.
pgrep "sleep" | xargs kill
車輪の再発明
投稿前にそういえば英語で検索してねーなと思って検索したらありました!合掌!
Stack Overflow:Best way to kill all child processes
いろんなやり方があるんだなぁ.