【问题标题】:Capturing Ctrl-c in ruby在 ruby​​ 中捕获 Ctrl-c
【发布时间】:2011-01-06 13:27:35
【问题描述】:

我通过了一个长期运行的遗留 ruby​​ 程序,它有很多次出现

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

贯穿始终。

如果不跟踪每个可能处理的异常(至少不是立即),我仍然希望能够不时使用 CtrlC.

而且我想以一种只添加到代码中的方式这样做(这样我就不会影响现有的行为,或者在运行过程中错过其他捕获的异常。)

[CtrlC 是 SIGINT 或 SystemExit,在 Ruby 的异常处理系统中似乎等同于 SignalException.new("INT")class SignalException < Exception,这就是为什么会出现这个问题。]

我想写的代码是:

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

编辑:只要您获得要捕获的异常的正确类,此代码就可以工作。这可以是 SystemExit、Interrupt 或 IRB::Abort,如下所示。

【问题讨论】:

    标签: ruby exception copy-paste


    【解决方案1】:

    在 Ruby 中干净地处理 Ctrl-C ZeroMQ 方式:

    #!/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
    

    Source

    【讨论】:

    • 很好的例子,但我认为它增加了比 OP 上下文中实际需要的复杂性更多。
    【解决方案2】:

    我使用ensure 效果很好!这是针对您希望在您的事情结束时发生的事情,无论它为什么结束。

    【讨论】:

      【解决方案3】:

      问题在于,当 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

      【讨论】:

      • Wayne,你能把 IRB::Abort 示例也添加到你的列表中吗?
      • @Tim,去找 irb.rb(在我的系统上,它在 /usr/lib/ruby/1.8/irb.rb 中)并找到主循环(搜索 @context.evaluate)。看看救援条款,我想你就会明白为什么 IRB 会这样做。
      • 谢谢。查看 irb.rb 中#signal_handle 的定义也有助于我理解。他们在主循环的异常变量绑定中也有一个巧妙的技巧。 (使用救援子句作为挑选特定异常的一种方式,然后在救援主体之外使用该异常。)
      • 这些作品完美:rescue SystemExit, Interrupt raise rescue Exception => e
      【解决方案4】:

      如果您可以包装整个程序,您可以执行以下操作:

       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! 立即退出,它不会引发异常,因此代码不会意外捕获它。

      【讨论】:

      • 请注意,IRB 中的 Ctrl-C 发送的是 IRB::Abort,而不是 SIGINT。否则@Logan 的答案是一个解决方案。
      • @TimSnowhite for ruby​​ 解释器 SIGINT 对我来说很好。
      • throw 和 catch 必须在同一个线程上,所以如果你想在另一个线程上捕获 Interrupt 异常,这是行不通的。
      【解决方案5】:

      如果您无法将整个应用程序包装在 begin ... rescue 块中(例如,Thor),您可以直接捕获 SIGINT

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

      130 是标准退出代码。

      【讨论】:

      • 仅供参考,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 上杀死线程,而不改变行为。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-22
      • 1970-01-01
      • 1970-01-01
      • 2012-06-14
      • 2014-01-09
      • 1970-01-01
      • 2015-04-17
      相关资源
      最近更新 更多