web-dev-qa-db-fra.com

Capturer Ctrl-c dans ruby

On m'a transmis un héritage de longue durée Ruby program, qui a de nombreuses occurrences de

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

tout au long.

Sans traquer toutes les exceptions possibles, celles-ci pourraient être gérées (du moins pas immédiatement), je voudrais quand même être en mesure de les arrêter parfois avec CtrlC.

Et je voudrais le faire d'une manière qui ne fait qu'ajouter au code (donc je n'affecte pas le comportement existant, ou je manque une exception interceptée au milieu d'une course.)

[CtrlC est SIGINT, ou SystemExit, qui semble être équivalent à SignalException.new("INT") dans le système de gestion des exceptions de Ruby. class SignalException < Exception, C'est pourquoi ce problème se pose.]

Le code que j'aimerais avoir écrit serait:

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

EDIT: ce code fonctionne, tant que vous obtenez la classe de l'exception que vous souhaitez intercepter correctement. C'est soit SystemExit, Interrupt ou IRB :: Abort comme ci-dessous.

100
Tim Snowhite

Le problème est que lorsqu'un programme Ruby se termine, il le fait en augmentant SystemExit. Lorsqu'un contrôle-C entre, il soulève Interruption =. Étant donné que SystemExit et Interrupt dérivent de Exception, votre gestion des exceptions arrête la sortie ou l'interruption de ses pistes. Voici le correctif:

Partout où vous le pouvez, changez

rescue Exception => e
  # ...
end

à

rescue StandardError => e
  # ...
end

pour ceux que vous ne pouvez pas changer en StandardError, relancez l'exception:

rescue Exception => e
  # ...
  raise
end

ou, à tout le moins, relancer SystemExit et Interrupt

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

Toutes les exceptions personnalisées que vous avez faites doivent dériver de StandardError, pas Exception.

123
Wayne Conrad

Si vous pouvez envelopper votre programme entier, vous pouvez faire quelque chose comme ceci:

 trap("SIGINT") { throw :ctrl_c }

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

Cela a essentiellement CtrlC utilisez catch/throw au lieu de la gestion des exceptions, donc à moins que le code existant n'ait déjà un catch: ctrl_c, ça devrait aller.

Alternativement, vous pouvez faire une trap("SIGINT") { exit! }. exit! Se ferme immédiatement, il ne déclenche pas d'exception, le code ne peut donc pas l'attraper accidentellement.

69
Logan Capaldo

Si vous ne pouvez pas envelopper votre demande dans un begin ... rescue bloquer (par exemple, Thor) vous pouvez simplement intercepter SIGINT:

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

130 est un code de sortie standard.

36
Erik Nomitch

J'utilise ensure à grand effet! C'est pour les choses que vous voulez que vos choses se terminent, peu importe pourquoi elles se terminent.

4
nroose

Gérer proprement Ctrl-C dans Ruby de la manière 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

Source

0
noraj