シェルにおいて、バックグラウンド実行のプロセスIDは$!で取得することができる。
sleep 10 &
echo $! # sleepコマンドのプロセスIDが取得できる。
では、コマンドがパイプで繋がれている場合はどうなるだろうか?
tail -f log.txt | grep 'keyword' &
echo $!
この場合、$!で取得されるのは、パイプの後半のプロセスgrepコマンドのプロセスになる。
ここで問題となるのはkill $!として、grepをキルしてもtailのプロセスが残ってしまうことである。
$ kill $! # grepのみがキルされる
$ ps aux | grep tail # tailのプロセスがまだ残っている
# > 9210 0.0 0.0 34122900 900 s000 SN 6:17PM 0:00.00 tail -f log.txt
前半のプロセスのプロセスIDを取得するにはどうすれば良いだろうか?
答えを言うと以下のようにすればよい。
tail -f log.txt > >(grep 'keyword') &
echo $! # $!はtailのプロセスIDになっている
ここで見慣れない文法 >( コマンド ) というものがある。
これはコマンド置換と呼ばれるbashの文法で、コマンドを実行したときのIOをファイルのように扱うことができるものである。
(参考:https://techblog.raccoon.ne.jp/archives/53726690.html )
つまり、tail -f ...の結果をリダイレクトした先は本来ならファイルであるはずだが、そのファイルへの書き出しがgrep ...の標準入力に渡されるようになる。
一般にプロセス置換を使うと cmd1 | cmd2 とパイプで繋ぐものと等価なものがcmd1 > >(cmd2)として実現できる。
しかし、パイプと違うのがバックグラウンド実行した際に$!で得られるプロセスID。コマンド置換を使った場合、前半のtailのコマンドのプロセスIDが取得される。
これによってkill $!としても、tailが残ってしまうことなく終了するし、ついでにgrepプロセスも終了する。
参考: https://stackoverflow.com/questions/1652680/how-to-get-the-pid-of-a-process-that-is-piped-to-another-process-in-bash
↧