Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get Ruby script to fail when shelled-out command returns with non-zero exit code?

In a Ruby script, there are various ways to call system commands / command lines

  1. backticks: `command arg1 arg2`
  2. delimited form, e.g. %x(command arg1 arg2) (other delimiters available)
  3. Kernel#system method: system('command arg1 arg2')
  4. 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.

like image 559
das-g Avatar asked Jun 19 '15 12:06

das-g


People also ask

How do I stop a Ruby script in terminal?

exit(false) which terminates execution immediately.

How do you exit code in Ruby?

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.


1 Answers

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.

like image 182
Max Avatar answered Oct 11 '22 05:10

Max