Захват Ctrl-c в ruby


мне передали давно запущенную программу legacy 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

EDIT: этот код работает, пока вы получаете класс исключения, которое вы хотите поймать правильно. Это либо SystemExit, прерывание, либо IRB:: прервать, как показано ниже.

4 98

4 ответа:

проблема в том, что когда программа Ruby заканчивается, она делает это, поднимая SystemExit. Когда приходит control-C, он поднимает отмена. Так как SystemExit и отмена выводим из исключение, ваша обработка исключений останавливает выход или прерывание на своих дорожках. Вот исправление:

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

rescue Exception => e
  # ...
end

до

rescue StandardError => e
  # ...
end

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

rescue Exception => e
  # ...
  raise
end

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

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

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

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

 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! немедленно завершает работу, он не вызывает исключение, поэтому код не может случайно поймать его.

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

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

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

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