简体   繁体   中英

Trap Ctrl-D in a Ruby Script with ARGF

I am currently using ARGV.gets to capture user input from the command line. I want to allow Ctrl-D terminate the script, but don't know how to do this using Signal.trap or through error handling. I tried to find a list of trap codes for something like Ctrl-D but was unable to find anything I was looking for. Likewise, rescuing Exception doesn't work because Ctrl-D doesn't raise an exception. Is there a trap code for Ctrl-D or any other way to detect this?

For example... I am currently able to detect Ctrl-C by trapping...

# Trap ^C 
Signal.trap("INT") { 
   # Do something 
   exit
}

or error handling...

def get_input
   input = ARGF.gets
   input.strip!
   rescue SystemExit, Interrupt => e
   # If we get here, Ctrl-C was encountered
end

However, I haven't been able to trap or detect Ctrl-D.

ARGF in just a special case of stream. Ctrl + D is just end of input.

With this in mind use method ARGF.eof? . Link to documentation

I am unsure of your use case but I am assuming you are intending to do something before the script exits. If so then your best bet is probably a bit easier than signal trapping. The Kernel Module actually offers you an #at_exit method that will be executed just prior to the program actually exiting.

Usage: (from Kernel#at_exit Docs )

def do_at_exit(str1)
  at_exit { print str1 }
end
at_exit { puts "cruel world" }
do_at_exit("goodbye ")
exit

"produces:"

goodbye cruel world

as you can see you can define multiple handlers which will be executed in reverse order when the program exits.

Since Kernel is included in Object you can handle Object specifics as well like

class People
  at_exit {puts "The #{self.name} have left"}
end
exit
# The People have left

or even on instances

p = People.new
p.send(:at_exit, &->{puts "We are leaving"})
# We are leaving
# The People have left

Additionally for more specific Object based implementations you can take a look at ObjectSpace.define_finalizer .

example of usage:

class Person
  def self.finalize(name)
    proc {puts "Goodbye Cruel World -#{name}"}
  end 
  def initialize(name)
    @name = name
    ObjectSpace.define_finalizer(self, self.class.finalize(@name))
  end
end

Usage:

p = Person.new("engineersmnky")
exit
# Goodbye Cruel World -engineersmnky

This may not be specifically what you want as this will fire when an Object is garbage collected as well (not great for ephemeral objects) but if you have objects that should exist throughout the entire application this could still be used similar to an at_exit . Example

# requiring WeakRef to allow garbage collection 
# See: https://ruby-doc.org/stdlib-2.3.3/libdoc/weakref/rdoc/WeakRef.html
require 'weakref' # 
p1 = Person.new("Engineer")
p2 = Person.new("Engineer's Monkey")
p2 = WeakRef.new(p2)
GC.start # just for this example
# Goodbye Cruel World -Engineer's Monkey
#=> nil
p2
#=> WeakRef::RefError: Invalid Reference - probably recycled
exit
# Goodbye Cruel World -Engineer

As you can see the defined finalizer for p2 fired because the Person was gc'd but the program has not exited yet. p1 's finalizer waited until exit to fire because it retained its reference throughout the application.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM