Menangkap Ctrl-c dalam ruby


107

Saya melewati program ruby ​​lama yang berjalan lama, yang memiliki banyak kejadian

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

sepanjang itu.

Tanpa melacak setiap kemungkinan pengecualian yang masing-masing dapat tangani (setidaknya tidak segera), saya masih ingin menutupnya sesekali CtrlC.

Dan saya ingin melakukannya dengan cara yang hanya menambah kode (jadi saya tidak memengaruhi perilaku yang ada, atau melewatkan pengecualian yang tertangkap di tengah proses.)

[ CtrlCadalah SIGINT, atau SystemExit, yang tampaknya setara dengan SignalException.new("INT")sistem penanganan pengecualian Ruby. class SignalException < Exception, itulah mengapa masalah ini muncul.]

Kode yang ingin saya tulis adalah:

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

EDIT: Kode ini berfungsi, selama Anda mendapatkan kelas pengecualian yang ingin Anda perangkap dengan benar. Itu adalah SystemExit, Interrupt, atau IRB :: Abort seperti di bawah ini.

Jawaban:


132

Masalahnya adalah ketika program Ruby berakhir, ia melakukannya dengan menaikkan SystemExit . Saat control-C masuk, Interrupt akan muncul . Karena SystemExit dan Interrupt berasal dari Exception , penanganan pengecualian Anda menghentikan pintu keluar atau interupsi di jalurnya. Berikut perbaikannya:

Dimanapun Anda bisa, ubahlah

rescue Exception => e
  # ...
end

untuk

rescue StandardError => e
  # ...
end

bagi mereka yang tidak dapat Anda ubah ke StandardError, ajukan kembali pengecualian:

rescue Exception => e
  # ...
  raise
end

atau, paling tidak, naikkan kembali SystemExit dan Interrupt

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

Setiap pengecualian khusus yang Anda buat harus berasal dari StandardError , bukan Exception .


1
Wayne, maukah Anda menambahkan contoh IRB :: Abort ke daftar Anda juga?
Tim Snowhite

1
@Tim, cari irb.rb (di sistem saya, ada di /usr/lib/ruby/1.8/irb.rb) dan temukan loop utama (cari @ context.evaluate). Lihatlah klausul penyelamatan dan saya pikir Anda akan mengerti mengapa IRB berperilaku seperti itu.
Wayne Conrad

Terima kasih. Melihat definisi #signal_handle di irb.rb membantu pemahaman saya juga. Mereka memiliki trik rapi dalam pengikatan variabel pengecualian juga. (Menggunakan klausul penyelamatan sebagai cara untuk memilih pengecualian tertentu, kemudian menggunakan pengecualian itu di luar badan penyelamat.)
Tim Snowhite

ini bekerja dengan sempurna:rescue SystemExit, Interrupt raise rescue Exception => e
James Tan

73

Jika Anda dapat menyelesaikan seluruh program, Anda dapat melakukan sesuatu seperti berikut:

 trap("SIGINT") { throw :ctrl_c }

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

Ini pada dasarnya memiliki CtrlCpenggunaan catch / throw alih-alih penanganan pengecualian, jadi kecuali kode yang ada sudah memiliki catch: ctrl_c di dalamnya, itu akan baik-baik saja.

Sebagai alternatif, Anda dapat melakukan a trap("SIGINT") { exit! }. exit!segera keluar, itu tidak memunculkan pengecualian sehingga kode tidak dapat menangkapnya secara tidak sengaja.


2
Perhatikan bahwa Ctrl-C di IRB mengirimkan IRB :: Abort, bukan SIGINT. Jika tidak, jawaban @ Logan adalah solusi.
Tim Snowhite

1
@TimSnowhite untuk penerjemah ruby SIGINTberfungsi dengan baik untuk saya.
defhlt

1
melempar dan menangkap harus berada di utas yang sama, jadi ini tidak akan berfungsi jika Anda ingin menangkap pengecualian Interupsi di utas lain.
Matt Connolly

39

Jika Anda tidak dapat membungkus seluruh aplikasi Anda dalam satu begin ... rescueblok (misalnya, Thor), Anda dapat menjebak SIGINT:

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

130 adalah kode keluar standar.


1
FYI, 130 adalah kode keluar yang benar untuk skrip yang diinterupsi 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))
Dorian

Sempurna! Saya memiliki server Sinatra yang miring dengan utas latar belakang yang terus berjalan, dan ini terlihat seperti yang saya perlukan untuk mematikan utas juga di cntrl-c, tanpa mengubah perilaku.
Narfanator

4

Saya menggunakan ensureuntuk efek yang bagus! Ini untuk hal-hal yang Anda inginkan terjadi ketika barang Anda berakhir tidak peduli mengapa itu berakhir.


0

Menangani Ctrl-C dengan rapi di Ruby dengan cara 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

Sumber


Contoh yang bagus, tapi saya pikir itu menambah kompleksitas lebih dari yang sebenarnya dibutuhkan dalam konteks OP.
Ron Klein
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.