[ACCEPTED]-Capturing Ctrl-c in ruby-copy-paste

Accepted answer
Score: 136

The problem is that when a Ruby program 9 ends, it does so by raising SystemExit. When a control-C 8 comes in, it raises Interrupt. Since both SystemExit and Interrupt derive 7 from Exception, your exception handling is stopping 6 the exit or interrupt in its tracks. Here's 5 the fix:

Wherever you can, change

rescue Exception => e
  # ...
end

to

rescue StandardError => e
  # ...
end

for those 4 you can't change to StandardError, re-raise 3 the exception:

rescue Exception => e
  # ...
  raise
end

or, at the very least, re-raise 2 SystemExit and Interrupt

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

Any custom exceptions 1 you have made should derive from StandardError, not Exception.

Score: 76

If you can wrap your whole program you can 7 do something like the following:

 trap("SIGINT") { throw :ctrl_c }

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

This basically 6 has CtrlC use catch/throw instead of exception 5 handling, so unless the existing code already 4 has a catch :ctrl_c in it, it should be 3 fine.

Alternatively you can do a trap("SIGINT") { exit! }. exit! exits 2 immediately, it does not raise an exception 1 so the code can't accidentally catch it.

Score: 42

If you can't wrap your whole application 2 in a begin ... rescue block (e.g., Thor) you can just trap 1 SIGINT:

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

130 is a standard exit code.

Score: 5

I am using ensure to great effect! This is for 2 things you want to have happen when your 1 stuff ends no matter why it ends.

Score: 1

Handling Ctrl-C cleanly in Ruby the ZeroMQ way:

#!/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

Score: 0

Perhaps the most simple solution?

Signal.trap('INT') { exit }

This is 5 what I use, it works. Put it somewhere before a 4 possible user interaction.

Here, a more verbose 3 solution, to print something to STDERR and 2 exit:

Signal.trap('INT') { abort 'Interrupted by user' }

See here for difference between exit and 1 abort.

More Related questions