Захват Ctrl-c в рубине

Мне была передана давно запущенная рубиновая программа, которая имеет многочисленные вхождения

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

во всем этом.

Не отслеживая каждое возможное исключение, каждое из них может обрабатываться (по крайней мере, не сразу), я все равно хотел бы время от времени закрывать его с помощью Ctrl C.

И я хотел бы сделать это таким образом, который добавит только код (поэтому я не влияю на существующее поведение или не пропущу исключение, исключенное иначе в середине прогона.)

[Ctrl C - это SIGINT или SystemExit, который, как представляется, эквивалентен SignalException.new("INT") в системе обработки исключений Ruby. class SignalException < Exception, поэтому эта проблема возникает.]

Код, который я хотел бы написать, будет:

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

РЕДАКТИРОВАТЬ: Этот код работает, если вы получите класс исключения, который вы хотите уловить. Это либо SystemExit, Interrupt, либо IRB:: Abort, как показано ниже.

Ответ 1

Проблема в том, что когда программа Ruby заканчивается, она делает это, поднимая SystemExit. Когда включается элемент управления-C, он вызывает прерывание. Поскольку как SystemExit, так и Interrupt производятся от Exception, ваша обработка исключений останавливает выход или прерывание на своих дорожках. Здесь исправление:

Везде, где вы можете, измените

rescue Exception => e
  # ...
end

to

rescue StandardError => e
  # ...
end

для тех, которые вы не можете изменить в StandardError, переподготовьте исключение:

rescue Exception => e
  # ...
  raise
end

или, по крайней мере, повторно поднять SystemExit и прерывание

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

Любые пользовательские исключения, которые вы сделали, должны выводиться из StandardError, а не из Exception.

Ответ 2

Если вы можете обернуть всю вашу программу, вы можете сделать что-то вроде следующего:

 trap("SIGINT") { throw :ctrl_c }

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

В основном это Ctrl C использует catch/throw вместо обработки исключений, поэтому, если существующий код уже не имеет catch: ctrl_c в нем, это должно быть хорошо.

В качестве альтернативы вы можете сделать trap("SIGINT") { exit! }. exit! выходит немедленно, он не вызывает исключения, поэтому код не может случайно его поймать.

Ответ 3

Если вы не можете обернуть все ваше приложение в блок begin ... rescue (например, Thor), вы можете просто уловить SIGINT:

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

130 - стандартный код выхода.

Ответ 4

Я использую ensure для отличного эффекта! Это для вещей, которые вы хотите, когда ваши вещи заканчиваются независимо от того, почему они заканчиваются.