RubyでCtrl-cをキャプチャする


107

長期間実行されているレガシーRubyプログラムに合格しました。

begin
  #dosomething
rescue Exception => e
  #halt the exception's progress
end

それを通して。

これらが処理する可能性のあるすべての例外を(少なくともすぐにではなく)追跡することなく、で時々それをシャットダウンできるようにしたいと思いCtrlCます。

そして、私はコードに追加するだけの方法でそうしたいと思います(そのため、既存の動作に影響を与えたり、実行中に途中でキャッチされた例外を見逃したりしません)。

[ CtrlCはSIGINT、つまりSystemExitでありSignalException.new("INT")、Rubyの例外処理システムと同等のようです。class SignalException < Exception、これがこの問題が発生する理由です。]

私が書きたいコードは次のようになります:

begin
  #dosomething
rescue SignalException => e
  raise e
rescue Exception => e
  #halt the exception's progress
end

編集:このコードは、トラップする例外のクラスを正しく取得する限り機能します。これは、以下のようにSystemExit、Interrupt、またはIRB :: Abortのいずれかです。

回答:


132

問題は、Rubyプログラムが終了すると、SystemExitが発生して終了することです。Control-Cが入ると、割り込みを発生させます。SystemExitInterruptはどちらもExceptionから派生しているため、例外処理はそのトラックの出口または割り込みを停止しています。ここに修正があります:

できる限り変更

rescue Exception => e
  # ...
end

rescue StandardError => e
  # ...
end

StandardErrorに変更できない場合は、例外を再度発生させます。

rescue Exception => e
  # ...
  raise
end

または、少なくとも、SystemExitとInterruptを再度発生させます

rescue SystemExit, Interrupt
  raise
rescue Exception => e
  #...
end

作成したカスタム例外はすべて、Exceptionではなく、StandardErrorから派生する必要があります


1
ウェイン、リストにIRB :: Abortの例も追加してくれませんか?
Tim Snowhite、2010年

1
@ Tim、irb.rbを見つけて(私のシステムでは/usr/lib/ruby/1.8/irb.rbにあります)、メインループを見つけます(@ context.evaluateを検索します)。レスキュー条項を見てください。IRBがそのように動作している理由が理解できると思います。
ウェインコンラッド

ありがとうございました。irb.rbの#signal_handleの定義を見ると、理解にも役立ちました。彼らはメインループの例外変数バインディングにも巧妙なトリックを持っています。(特定の例外を特定する方法としてレスキュー条項を使用してから、その例外をレスキューボディの外で使用する。)
Tim Snowhite 2010年

これらは完璧に機能します:rescue SystemExit, Interrupt raise rescue Exception => e
James Tan

73

プログラム全体をラップできる場合は、次のようなことができます。

 trap("SIGINT") { throw :ctrl_c }

 catch :ctrl_c do
 begin
    sleep(10)
 rescue Exception
    puts "Not printed"
 end
 end

これは基本的CtrlCに例外処理の代わりにキャッチ/スローを使用するため、既存のコードにすでにcatch:ctrl_cが含まれていない限り、問題ありません。

または、を実行できますtrap("SIGINT") { exit! }exit!すぐに終了し、例外を発生させないため、コードが誤ってキャッチすることはありません。


2
IRBのCtrl-Cは、SIGINTではなくIRB :: Abortを送信することに注意してください。そうでなければ@Loganの答えは解決策です。
Tim Snowhite、2010年

1
Rubyインタープリター用の@TimSnowhiteはSIGINT、私には問題なく動作します。

1
スローとキャッチは同じスレッド上にある必要があるため、別のスレッドで割り込み例外をキャッチする場合は機能しません。
Matt Connolly、

39

アプリケーション全体をbegin ... rescueブロック(たとえば、トール)でラップできない場合は、次のようにトラップできますSIGINT

trap "SIGINT" do
  puts "Exiting"
  exit 130
end

130は標準の終了コードです。


1
FYI、130は、スクリプトを中断はCtrl-Cの正しい終了コードである:google.com/search?q=130+exit+code&en=130 | Script terminated by Control-C | Ctl-C | Control-C is fatal error signal 2, (130 = 128 + 2, see above)
ドリアン

パーフェクト!私は常に実行中のバックグラウンドスレッドを備えた不安定なSinatraサーバーを持っています。これは、他の動作を変更せずにcntrl-cでスレッドを強制終了するために必要なもののように見えます。
-Narfanator

4

ensure効果的に使っています!これは、なぜ終了するのかに関わらず、何かが終了したときに発生させたいことのためのものです。


0

RubyのZeroMQ でCtrl-Cをきれいに処理する:

#!/usr/bin/env ruby

# Shows how to handle Ctrl-C
require 'ffi-rzmq'

context = ZMQ::Context.new(1)
socket = context.socket(ZMQ::REP)
socket.bind("tcp://*:5558")

trap("INT") { puts "Shutting down."; socket.close; context.terminate; exit}

puts "Starting up"

while true do
  message = socket.recv_string
  puts "Message: #{message.inspect}"
  socket.send_string("Message received")
end

ソース


良い例ですが、OPのコンテキストで実際に必要なものよりも複雑になると思います。
ロンクライン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.