In a Ruby script, there are various ways to call system commands / command lines
`command arg1 arg2`
%x(command arg1 arg2)
(other delimiters available)Kernel#system
method: system('command arg1 arg2')
Kernel#exec
method: exec('command arg1 arg2')
If I want the Ruby script to fail (with an exception) when a called command fails (with a non-zero exit code) I can either check for the exit code in the special variable $?
for the first two variants:
`command arg1 arg2`
fail unless $? == 0
or
%x,command arg1 arg2,
fail unless $? == 0
If I'm fine with the command's standard output going to the Ruby script's standard output (and I am), I can use variant 3 and check its return value:
unless system('command arg1 arg2')
fail
end
If I don't care about the ability to rescue the exception nor about the stacktrace printing behavior of unrescued exceptions, I can of course use exit(1)
or in the first two variants exit($?)
in place of fail
.
If further the execution of the command is the last thing the Ruby script should do, even when the command succeeds (exit code 0
), I can use the fourth variant:
exec('command arg1 arg2')
This will replace the Ruby process with the new process created by invoking the command, but the effect for the caller of the Ruby script will be the same: He sees a non-zero exit code exactly if the called command caused a non-zero exit code.
I very much like the conciseness of the fourth variant, but if executing the command isn't the last thing to do in case it succeeds, I can unfortunately not use it. The conditional fail
or exit
invocations of the other variants look very unclean in comparison and in one usecase of mine more often than not violate the single level of abstraction and single responsibility principles.
I could of course easily write a wrapper function for any of the first three approaches to make their usage look just as concise, but as this seems like such a fundamental modus operandi, I wondered whether Ruby already has something like that built in ... be it a utility function I could use instead of a wrapper of my own, or a mechanism that changes the behavior of one or several of the command invocation methods to cause an error or a non-zero exit when a command fails, analogous to sh's and bash's option to set -e
.
exit(false) which terminates execution immediately.
Ruby and exit codes Ruby's default exit value is 0 if we want to send a different exit value we should use the exit method. The exit method receives as a parameter a true or false value if the value is an integer that would be the returned value of our script.
As far as I know there is no built-in way to do this, but you can almost get the behavior you want with a little metaprogramming magic
def set_exit enable
if enable
define_method :system do |*args|
Kernel.system *args
exit $?.exitstatus unless $?.success?
end
else
define_method :system, Kernel.instance_method(:system)
end
end
set_exit true
# ...
# any failed system calls here will cause your script to exit
# ...
set_exit false
# now system is back to normal
This works by redefining system
for Object
, while explicitly using Kernel.system
when the built-in behavior is needed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With