Ruby'de Ctrl-c'yi yakalama


107

Uzun süredir devam eden eski bir yakut programından geçtim.

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

boyunca.

Bunların her birinin üstesinden gelebilecek olası istisnaları takip etmeden (en azından hemen değil), yine de zaman zaman kapatabilmek istiyorum CtrlC.

Ve bunu yalnızca koda eklenecek bir şekilde yapmak istiyorum (bu nedenle mevcut davranışı etkilemem veya çalışmanın ortasında başka türlü yakalanan bir istisnayı kaçırmam.)

[ Ruby'nin istisna işleme sistemine CtrlCeşdeğer görünen SIGINT veya SystemExit'tir SignalException.new("INT"). class SignalException < Exceptionişte bu yüzden bu sorun ortaya çıkıyor.]

Yazmak istediğim kod şöyle olurdu:

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

DÜZENLEME: Bu kod, doğru tuzağa düşürmek istediğiniz istisna sınıfını aldığınız sürece çalışır. Bu, aşağıdaki gibi SystemExit, Interrupt veya IRB :: Abort.

Yanıtlar:


132

Buradaki sorun, bir Ruby programı bittiğinde, bunu SystemExit'i yükselterek yapmasıdır . Bir kontrol-C geldiğinde, Interrupt'ı yükseltir . Her iki yana SystemExit ve kesme türeyen İstisna , istisna işleme kendi parçalarını çıkış veya kesme durduruluyor. İşte düzeltme:

Nerede yapabilirsen değiştir

rescue Exception => e
  # ...
end

-e

rescue StandardError => e
  # ...
end

StandardError'a değiştiremeyecekleriniz için istisnayı yeniden yükseltin:

rescue Exception => e
  # ...
  raise
end

veya en azından SystemExit ve Interrupt'ı yeniden yükseltin

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

Yaptığınız tüm özel istisnalar türetilmesi StandardError değil Exception .


1
Wayne, listene bir IRB :: Abort örneği de eklemek ister misin?
Tim Snowhite

1
@Tim, gidip irb.rb'yi bul (benim sistemimde /usr/lib/ruby/1.8/irb.rb) ve ana döngüyü bul (@ context.evaluate için arama yapın). Kurtarma maddelerine bakın ve bence IRB'nin neden böyle davrandığını anlayacaksınız.
Wayne Conrad

teşekkür ederim. İrb.rb'deki #signal_handle'ın tanımına bakmak da anlamama yardımcı oldu. Ana döngünün istisna değişken bağlamasında da düzgün bir hileye sahipler. (Kurtarma maddelerini belirli bir istisnayı seçmenin bir yolu olarak kullanmak ve ardından bu istisnayı kurtarma organlarının dışında kullanmak.)
Tim Snowhite

bu işler mükemmel:rescue SystemExit, Interrupt raise rescue Exception => e
James Tan

73

Tüm programınızı tamamlayabilirseniz, aşağıdaki gibi bir şey yapabilirsiniz:

 trap("SIGINT") { throw :ctrl_c }

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

Bu temelde CtrlCistisna işleme yerine yakalama / fırlatma özelliğine sahiptir , bu nedenle mevcut kodda zaten bir catch: ctrl_c yoksa, iyi olmalıdır.

Alternatif olarak, bir trap("SIGINT") { exit! }. exit!hemen çıkar, bir istisna oluşturmaz, böylece kod yanlışlıkla onu yakalayamaz.


2
IRB'de Ctrl-C'nin SIGINT'i değil IRB :: Abort'u gönderdiğine dikkat edin. Aksi takdirde @ Logan'ın cevabı bir çözümdür.
Tim Snowhite

1
Ruby tercümanı için @TimSnowhite benim için SIGINTiyi çalışıyor.
defhlt

1
throw ve catch aynı iş parçacığında olmalıdır, bu nedenle Interrupt istisnasını başka bir iş parçacığında yakalamak istiyorsanız bu çalışmayacaktır.
Matt Connolly

39

Tüm uygulamanızı bir begin ... rescuebloğa sığdıramıyorsanız (örneğin, Thor) şunları yakalayabilirsiniz SIGINT:

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

130, standart bir çıkış kodudur.


1
Bilginize, 130, Ctrl-C kesintiye uğrayan komut dosyaları için doğru çıkış kodudur: 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

Mükemmel! Sürekli çalışan bir arka plan iş parçacığına sahip tehlikeli bir Sinatra sunucum var ve bu, iş parçacığını başka türlü davranışı değiştirmeden cntrl-c'de de sonlandırmam gereken şeye benziyor.
Narfanator

4

Ben ensureharika bir etki için kullanıyorum ! Bu, neden biterse bitsin, eşyalarınız bittiğinde olmasını istediğiniz şeyler içindir.


0

Ruby'de Ctrl-C'yi ZeroMQ yöntemiyle temiz bir şekilde kullanma:

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

Kaynak


Güzel örnek, ama bence OP bağlamında gerçekten ihtiyaç duyulandan daha fazla karmaşıklık katıyor.
Ron Klein
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.