在红宝石中捕获Ctrl-c


107

我通过了一个长期运行的旧式红宝石程序,该程序多次出现

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,如下所示。

Answers:


132

问题是,当Ruby程序结束时,它通过提高SystemExit来完成。当Control-C进入时,它将引发Interrupt。由于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

您所做的任何自定义异常都应源自StandardError而不是Exception


1
韦恩,您是否也愿意在列表中添加IRB :: Abort示例?
蒂姆·斯诺怀特

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 / throw而不是异常处理,因此,除非现有代码中已经具有catch:ctrl_c,否则应该没问题。

或者,您可以做一个trap("SIGINT") { exit! }exit!立即退出,它不会引发异常,因此代码不会意外捕获它。


2
请注意,IRB中的Ctrl-C发送IRB :: Abort,而不是SIGINT。否则,@ Logan的答案是一种解决方案。
Tim Snowhite

1
@TimSnowhite的红宝石翻译SIGINT对我来说很好。
2012年

1
throw和catch必须在同一线程上,因此如果要在另一个线程上捕获Interrupt异常,则此方法将无效。
马特·康诺利

39

如果您不能将整个应用程序包装在一个begin ... rescue块中(例如,Thor),则可以捕获SIGINT

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

130是标准退出代码。


1
仅供参考,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)
Dorian

完善!我有一个运行着不断运行的后台线程的奇妙的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上下文实际所需的复杂性。
罗恩·克莱恩
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.