Ruby外部プログラム実行でのSIGINTの扱い
system関数を使って外部プログラムを実行しているときに、Rubyスクリプトに対してCtrl-C(SIGINT)を打っても、system関数で実行している外部プログラムを止めるだけで、Rubyスクリプトを止めることはできない。ループでsystem関数を連続的に何度も実行すると、何度もCtrl-Cを打たなければならなくなる。
次の例だと10回Ctrl-Cを打たなければならない。
10.times { |i| system "sleep 2; echo #{i}" }
Ctrl-C一発でRubyスクリプト、外部プログラムの両方を止めるにはforkしてexecすればよい。RubyスクリプトはCtrl-Cを受け取った後、子プロセスを中断した後で、自身を停止するように振舞う。
10.times { |i| pid = fork { exec "sleep 2; echo #{i}" } exitpidf, status = Process.waitpid2 pid if !status.exited? puts "error: process exit" end }
`プログラム`で実行すればforkしなくても1回のCtrl-Cで終了できるけど、画面出力のタイミングが遅れてしまうので、すなおにforkしたほうが良いだろう。
でも、なんでsystemと`バッククォート`で取り扱われ方が違うのだろうか。たしかにバッククォートで外部プログラムを実行している時に中断すると出力がきちんと得られないからRubyスクリプトも止める、ってのはわかる。だけどsystemの場合にRubyスクリプトを止めない理由がわからない。Rubyスクリプトも止める方法はforkしてexecすれば良いから、そのほかの外部プログラム実行パターンを提供するために、今のsystemがこのような仕様になっているのかな?