I need to execute something when the user shuts down the script, or presses ctr c in the terminal, in ruby. How can I accomplish this?
For example:
def epicFunction()
puts("User closed down the script")
end
catch (user exitted the script) do
epicFunction()
end
CodePudding user response:
Use #at_exit Blocks
You can use Kernel#at_exit to register one or more blocks to run at exit. These will execute whenever the program exits regardless of why it exited, such as an exception being raised, a call to Kernel#exit like exit 1
, receiving a signal like SIGTERM, or even just a normal exit of the interpreter when it reaches the end of a script or program. I can't think of an example of when the #at_exit handlers wouldn't be called (maybe someone knows of a special edge case) so they are pretty much guaranteed to be called regardless of how the program exits, and since they don't rely on code paths like #exit does or on trapping signals they offer a stronger guarantee of being called right before the interpreter exits. It might help to think of #at_exit somewhat like Bash's trap EXIT
as a related example, even if the languages and implementations are fundamentally different.
However, keep your #at_exit handlers as simple as you can. If your #at_exit handlers are complicated code and raise errors themselves due to bugs or scope issues, this can be rather hard to debug. As an somewhat contrived example, consider:
at_exit { puts bar }
raise "foo"
$ ruby at_exit.rb
at_exit.rb:1:in `block in <main>': undefined local variable or method `bar' for main:Object (NameError)
at_exit { puts bar }
^^^
at_exit.rb:3:in `<main>': foo (RuntimeError)
at_exit.rb:3:in `<main>': foo (RuntimeError)
Because the block is really just an anonymous closure, in this case it raises NameError when referencing bar when it gets called at exit. So you end up with NameError raised inside a Proc object, and then the expected RuntimeError raised by the call to raise
that triggered the #at_exit call in the first place.
In general, #at_exit should be reserved for simple things like printing messages, cleaning up temporary files or directories, or other basic housekeeping tasks that (for whatever reason) you chose not to scope within a block or method, or that Ruby doesn't clean up automagically. There aren't many things that really require exit handlers, but if you want them, #at_exit is the way to go.