Chụp Ctrl-c trong ruby


107

Tôi đã được thông qua một chương trình ruby ​​kế thừa đang chạy lâu dài, có rất nhiều lần xuất hiện

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

trong suốt nó.

Nếu không theo dõi mọi ngoại lệ có thể xảy ra mà mỗi ngoại lệ này có thể được xử lý (ít nhất là không phải ngay lập tức), tôi vẫn muốn có thể tắt nó đôi khi CtrlC.

Và tôi muốn làm như vậy theo cách chỉ thêm vào mã (vì vậy tôi không ảnh hưởng đến hành vi hiện có hoặc bỏ lỡ một ngoại lệ bị bắt gặp khác khi đang chạy.)

[ CtrlClà SIGINT, hoặc SystemExit, có vẻ tương đương với SignalException.new("INT")trong hệ thống xử lý ngoại lệ của Ruby. class SignalException < Exception, đó là lý do tại sao vấn đề này xuất hiện.]

Đoạn mã tôi muốn viết sẽ là:

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

CHỈNH SỬA: Mã này hoạt động, miễn là bạn nhận được đúng lớp của ngoại lệ bạn muốn bẫy. Đó là SystemExit, Interrupt hoặc IRB :: Abort như bên dưới.

Câu trả lời:


132

Vấn đề là khi một chương trình Ruby kết thúc, nó sẽ làm như vậy bằng cách nâng SystemExit lên . Khi một điều khiển-C đi vào, nó làm tăng Ngắt . Vì cả SystemExitInterrupt đều xuất phát từ Exception , nên việc xử lý ngoại lệ của bạn đang dừng việc thoát hoặc ngắt trong các đường của nó. Đây là bản sửa lỗi:

Bất cứ nơi nào bạn có thể, hãy thay đổi

rescue Exception => e
  # ...
end

đến

rescue StandardError => e
  # ...
end

đối với những người bạn không thể thay đổi thành StandardError, hãy nâng lại ngoại lệ:

rescue Exception => e
  # ...
  raise
end

hoặc ít nhất là nâng cao lại SystemExit và Interrupt

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

Bất kỳ ngoại lệ tùy chỉnh nào bạn đã thực hiện phải bắt nguồn từ StandardError , không phải Ngoại lệ .


1
Wayne, bạn có vui lòng thêm một ví dụ IRB :: Abort vào danh sách của bạn không?
Tim Snowhite

1
@Tim, hãy tìm irb.rb (trên hệ thống của tôi, nó nằm trong /usr/lib/ruby/1.8/irb.rb) và tìm vòng lặp chính (tìm kiếm @ context.evaluate). Nhìn vào các điều khoản giải cứu và tôi nghĩ bạn sẽ hiểu tại sao IRB lại hành xử theo cách mà nó làm.
Wayne Conrad

cảm ơn bạn. Nhìn vào định nghĩa cho #signal_handle trong irb.rb cũng giúp tôi hiểu rõ hơn. Họ có một mẹo nhỏ trong vòng lặp chính là ràng buộc biến ngoại lệ. (Sử dụng các điều khoản cứu như một cách để chọn ra một ngoại lệ đặc biệt, sau đó sử dụng mà ngoại lệ bên ngoài của các cơ quan cứu hộ.)
Tim Snowhite

những tác phẩm này hoàn hảo:rescue SystemExit, Interrupt raise rescue Exception => e
James Tan

73

Nếu bạn có thể kết thúc toàn bộ chương trình của mình, bạn có thể làm như sau:

 trap("SIGINT") { throw :ctrl_c }

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

Điều này về cơ bản có CtrlCsử dụng bắt / ném thay vì xử lý ngoại lệ, vì vậy trừ khi mã hiện tại đã có bắt: ctrl_c trong đó, nó sẽ ổn.

Ngoài ra, bạn có thể làm một trap("SIGINT") { exit! }. exit!thoát ngay lập tức, nó không đưa ra một ngoại lệ để mã không thể vô tình bắt được nó.


2
Lưu ý rằng Ctrl-C trong IRB sẽ gửi IRB :: Abort, không phải SIGINT. Nếu không thì câu trả lời của @ Logan là một giải pháp.
Tim Snowhite

1
@TimSnowhite cho SIGINTtrình thông dịch viên ruby phù hợp với tôi.
defhlt

1
ném và bắt phải trên cùng một luồng, vì vậy điều này sẽ không hoạt động nếu bạn muốn bắt ngoại lệ Ngắt trên một luồng khác.
Matt Connolly

39

Nếu bạn không thể gói toàn bộ ứng dụng của mình trong một begin ... rescuekhối (ví dụ: Thor), bạn có thể đặt bẫy SIGINT:

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

130 là một mã thoát tiêu chuẩn.


1
FYI, 130 là mã thoát chính xác cho các tập lệnh bị gián đoạn 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

Hoàn hảo! Tôi có một máy chủ Sinatra winky với một chuỗi nền chạy liên tục và điều này có vẻ như những gì tôi cần để hủy chuỗi đó trên cntrl-c, mà không thay đổi hành vi.
Narfanator

4

Tôi đang sử dụng ensuređể có hiệu quả tuyệt vời! Điều này dành cho những thứ bạn muốn xảy ra khi nội dung của bạn kết thúc bất kể lý do tại sao nó kết thúc.


0

Xử lý sạch Ctrl-C trong Ruby theo cách 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

Nguồn


Ví dụ đẹp, nhưng tôi nghĩ rằng nó thêm phức tạp hơn thực tế cần thiết trong bối cảnh của OP.
Ron Klein
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.