루비에서 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는 아래와 같습니다.

답변:


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

사용자 정의 예외는 Exception이 아니라 StandardError 에서 파생되어야합니다 .


1
Wayne, IRB :: Abort 예제도 목록에 추가해 주시겠습니까?
Tim Snowhite

1
@Tim, irb.rb (내 시스템에서는 /usr/lib/ruby/1.8/irb.rb에 있음)를 찾아서 메인 루프를 찾습니다 (@ context.evaluate 검색). 구조 조항을 보면 IRB가 왜 그렇게 행동하는지 이해할 수있을 것입니다.
Wayne Conrad

감사합니다. irb.rb의 #signal_handle에 대한 정의를 살펴보면 이해에 도움이되었습니다. 메인 루프의 예외 변수 바인딩에도 깔끔한 트릭이 있습니다. (특정 예외를 선택하는 방법으로 구조 조항을 사용한 다음 구조 기관 외부에서 해당 예외를 사용합니다.)
Tim Snowhite

이 완벽하게 작동합니다rescue SystemExit, Interrupt raise rescue Exception => e
제임스 탄에게

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는 SIGINT가 아닌 IRB :: Abort를 보냅니다. 그렇지 않으면 @Logan의 대답이 해결책입니다.
Tim Snowhite

1
루비 인터프리터에 대한 @TimSnowhite SIGINT가 잘 작동합니다.
defhlt

1
throw 및 catch는 동일한 스레드에 있어야하므로 다른 스레드에서 Interrupt 예외를 포착하려는 경우 작동하지 않습니다.
Matt Connolly

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의 맥락에서 실제로 필요한 것보다 더 복잡하다고 생각합니다.
Ron Klein
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.