Capturing Ctrl-C in Ruby

Capturing Ctrl-c in ruby

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

Wherever you can, change

rescue Exception => e
# ...
end

to

rescue StandardError => e
# ...
end

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

rescue Exception => e
# ...
raise
end

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

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

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

Ruby: Nice text on ^C (ctrl+C) command-line interrupt

Doing ctrl + c simply sends SIGINT signal to the given ruby process. You can intercept it by rescuing Interrupt:

begin
loop do
puts "foo bar baz"
end
rescue Interrupt
puts "\nExiting..."
end

How to disable control-c in ruby

you can trap SIGINT like this

trap "SIGINT" do
# this is called when you press control-c
# be very careful, you can't kill this program with control-c
end

You can also see Capturing Ctrl-c in ruby for some other ways to interact with control-c

How to capture Ctrl+C in a Python script which executes a Ruby script?

In your Ruby script:

trap('SIGINT') { exit 1 }

In your Python script, os.system() should return the value passed to exit. You can use that redirect the flow of control however needed, e.g. call sys.exit().

Ctrl-C doesn't quit program when using sytem() in Ruby

Kernel#system executes a given command in a sub-shell. Which means that Ctrl-C is going to that subshell and not your ruby script, so there's no use trapping it there. However, the command returns a value which indicates, whether it was completed successfully or not. When the command is stopped via Ctrl-C, it won't count as a successful completion. So, check the return value.

5.times do |i|
puts "Iteration #{i}"
success = system("sleep 3")
break unless success
end

Rescue/capture ctrl-break

ctrl-break sends SIGBREAK, so this would be the expected way to handle it:

trap("BREAK") { puts "ctrl-break" }

Unfortunately, Ruby doesn't know (SIG)BREAK, so you have to use the signal number instead, which should be 21:

trap(21) { puts "ctrl-break" }

ctrl-c can be handled accordingly, i.e.:

trap("INT") { puts "ctrl-c" }

Ctrl-c no longer kills my program

If your program runs on some flavor of Unix, you could catch the INT signal(sent by Ctrl-C) and call exit explicitly.

Put this before or after your begin block:

Signal.trap('INT') { exit 0 }

Additionally you could do some cleanup before exiting your program:

Signal.trap('INT') do
# some cleanup here
puts "Interrupted by user"
exit 0
end

More on signals in Ruby

Unix signals list

How do I configure ruby to enter the debugger on Ctrl-C (SIGINT)?

If you want to trap SIGINT while running in the console, the short answer is: you cannot unless you monkey-patch IRB. Every Ruby app (whether padrino, or rails or whatnot) that uses the console will end up calling usr/lib/ruby/1.9.1/irb.rb, and in IRB.start, it does:

trap("SIGINT") do
irb.signal_handle
end

... just before entering the main loop. This will override any trap("SIGINT") you might have put in your startup code.

But if you want to trap SIGINT in a script file (for example, if you want to profile your code as described by Mike Dunlavey here), you can create a script file such as:

# File: profile_complex_operation.rb
trap("SIGINT") { debugger }
MyApp.complex_operation

and then invoke it as in:

$ ruby profile_complex_operation.rb

Now, when you hit ^C (or send SIGINT from another process), it will enter the debugger.



Related Topics



Leave a reply



Submit