Capturando Ctrl-c en ruby


Se me pasó un programa ruby heredado de larga duración, que tiene numerosas ocurrencias de

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

A lo largo de ella.

Sin rastrear todas las excepciones posibles, cada una de ellas podría estar manejando (al menos no inmediatamente), todavía me gustaría poder apagarlo a veces con CtrlC.

Y me gustaría hacerlo de una manera que solo se agrega al código (para que no afecte el comportamiento existente, o se pierda una excepción atrapada en el medio de una carrera.)

[CtrlC es SIGINT, o SystemExit, que parece ser equivalente a SignalException.new("INT") en el sistema de manejo de excepciones de Ruby. class SignalException < Exception, por lo que surge este problema.]

El código que me gustaría haber escrito sería:

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

EDITAR: Este código funciona, siempre y cuando obtenga la clase de la excepción que desea atrapar correcta. Eso es SystemExit, Interrupt o IRB:: Abort como se muestra a continuación.

Author: Cœur, 2010-01-19

4 answers

El problema es que cuando un programa Ruby termina, lo hace levantando SystemExit. Cuando entra un control-C, levanta Interrupt . Dado que tanto SystemExitcomo Interruptderivan de Exception, su manejo de excepciones detiene la salida o interrupción en sus pistas. Aquí está la solución:

Donde puedas, cambia

rescue Exception => e
  # ...
end

A

rescue StandardError => e
  # ...
end

Para aquellos que no puede cambiar a StandardError, vuelva a levantar el excepción:

rescue Exception => e
  # ...
  raise
end

O, como mínimo, volver a elevar SystemExit e Interrumpir

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

Cualquier excepción personalizada que haya hecho debe derivar de StandardError, no Exception.

 117
Author: Wayne Conrad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-06 13:36:54

Si puedes envolver todo tu programa puedes hacer algo como lo siguiente:

 trap("SIGINT") { throw :ctrl_c }

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

Esto básicamente tiene CtrlC use catch/throw en lugar de exception handling, así que a menos que el código existente ya tenga un catch :ctrl_c en él, debería estar bien.

Alternativamente puede hacer un trap("SIGINT") { exit! }. exit! sale inmediatamente, no genera una excepción por lo que el código no puede atraparlo accidentalmente.

 66
Author: Logan Capaldo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-18 04:52:03

Si no puedes envolver toda tu aplicación en un bloque begin ... rescue (por ejemplo, Thor) puedes simplemente atrapar SIGINT:

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

130 es un código de salida estándar.

 27
Author: Erik Nomitch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-23 16:26:35

Estoy usando ensure con gran efecto! Esto es para las cosas que quieres que sucedan cuando tus cosas terminen sin importar por qué terminen.

 3
Author: nroose,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-24 23:52:56