Захоплення 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 :: Скасувати, як показано нижче.

Відповіді:


132

Проблема полягає в тому, що коли програма Ruby закінчується, вона робить це, піднімаючи SystemExit . Коли контрольний-C надходить, він викликає Interrupt . Оскільки і SystemExit, і Interrupt походять від Exception , ваша обробка винятків зупиняє вихід або переривання в своїх треках. Ось виправлення:

Де можна, зміни

rescue Exception => e
  # ...
end

до

rescue StandardError => e
  # ...
end

для тих, кого ви не можете змінити на StandardError, повторно підняйте виняток:

rescue Exception => e
  # ...
  raise
end

або, принаймні, знову підняти SystemExit і Перервати

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

Будь-які спеціальні винятки, які ви зробили, повинні випливати з StandardError , а не виняток .


1
Уейн, ти був би таким люб'язним, щоб додати приклад IRB :: Скасувати приклад у свій список?
Тім Снігур

1
@Tim, перейдіть до irb.rb (у моїй системі це в /usr/lib/ruby/1.8/irb.rb) і знайдіть основний цикл (пошук @ context.evaluate). Подивіться на рятувальні пропозиції, і я думаю, ви зрозумієте, чому IRB поводиться так, як це робить.
Уейн Конрад

спасибі. Перегляд визначення для #signal_handle в irb.rb також допоміг моєму розумінню. У них є акуратний трюк і в основній петлі. (Використовуючи рятувальні пункти як спосіб вибору конкретного винятку, а потім використовуючи цей виняток поза рятувальними органами.)
Тім Снігурі

ці роботи ідеально: 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 / кидання замість обробки винятків, тому, якщо вже не існує наявного коду: ctrl_c, він повинен бути добре.

Крім того, ви можете зробити це trap("SIGINT") { exit! }. exit!виходить негайно, він не викликає винятку, тому код не може випадково його зафіксувати.


2
Зауважте, що Ctrl-C в IRB надсилає IRB :: Abort, а не SIGINT. Інакше відповідь @ Логана - це рішення.
Тім Снігур,

1
@TimSnowhite для рубінового перекладача SIGINTдобре працює для мене.
дефлот

1
"кидок" і "catch" повинні бути на одній нитці, тому це не спрацює, якщо ви хочете вийняти виняток "Перерва" на іншому потоці.
Метт Конноллі

39

Якщо ви не можете завернути всю програму в begin ... rescueблок (наприклад, Thor), ви можете просто потрапити в пастку SIGINT:

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

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


1
FYI, 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, не змінюючи поведінки.
Нарфанатор

4

Я використовую ensureвеликий ефект! Це стосується тих речей, які ви хочете мати, коли ваші речі закінчуються незалежно від того, чому вони закінчуються.


0

Операція з Ctrl-C чисто в режимі Ruby 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

Джерело


Хороший приклад, але я думаю, що це додає більшої складності, ніж насправді потрібно в контексті ОП.
Рон Кляйн
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.