128

I have a bunch of system calls in ruby such as the following and I want to check their exit codes simultaneously so that my script exits out if that command fails.

system("VBoxManage createvm --name test1")
system("ruby test.rb")

I want something like

system("VBoxManage createvm --name test1", 0) <-- where the second parameter checks the exit code and confirms that that system call was successful, and if not, it'll raise an error or do something of that sort.

Is that possible at all?

I've tried something along the lines of this and that didn't work either.

system("ruby test.rb")
system("echo $?")

or

`ruby test.rb`
exit_code = `echo $?`
if exit_code != 0
  raise 'Exit code is not zero'
end
2
  • 3
    possible duplicate of Catching command-line errors using %x Commented Sep 10, 2013 at 20:24
  • In the above example, exit_code will be a string - either "0\n" or "1\n", so exit_code != 0 will always be true Commented Mar 22, 2015 at 22:47

7 Answers 7

189

From the documentation:

system returns true if the command gives zero exit status, false for non zero exit status. Returns nil if command execution fails.

system("unknown command")     #=> nil
system("echo foo")            #=> true
system("echo foo | grep bar") #=> false

Furthermore

An error status is available in $?.

system("VBoxManage createvm --invalid-option")

$?             #=> #<Process::Status: pid 9926 exit 2>
$?.exitstatus  #=> 2
Sign up to request clarification or add additional context in comments.

3 Comments

and how to capture its the output (not exit code) to a variable?
If you are in a rails console, testing this out, just keep in mind you may lose the value of $? so you need to capture it as part of your REPL command [10] pry(main)> system("touch /root/test 2> /dev/null") => false [11] pry(main)> $?.exitstatus => 0 [12] pry(main)> system("touch /root/test 2> /dev/null"); $?.exitstatus => 1
Another excellent comparison of system, backticks, %x, and exec provided here: stackoverflow.com/questions/6338908/…
54

For me, I preferred use `` to call the shell commands and check $? to get process status. The $? is a process status object, you can get the command's process information from this object, including: status code, execution status, pid, etc.

Some useful methods of the $? object:

   $?.exitstatus #=> return error code    
   $?.success?   #=> return true if error code is 0, otherwise false
   $?.pid        #=> created process pid

2 Comments

With the help of Rubocop, I found out that the readable alias for $? is $CHILD_STATUS
thanks, I had no idea $? was a thing in ruby too!
34

system returns false if the command has an non-zero exit code, or nil if there is no command.

Therefore

system( "foo" ) or exit

or

system( "foo" ) or raise "Something went wrong with foo"

should work, and are reasonably concise.

1 Comment

I liked this technique, super simple
7

You're not capturing the result of your system call, which is where the result code is returned:

exit_code = system("ruby test.rb")

Remember each system call or equivalent, which includes the backtick-method, spawns a new shell, so it's not possible to capture the result of a previous shell's environment. In this case exit_code is true if everything worked out, nil otherwise.

The popen3 command provides more low-level detail.

1 Comment

Open3.capture3 is a particularly easy method to use for this sort of task.
6

One way to do this is to chain them using and or &&:

system("VBoxManage createvm --name test1") and system("ruby test.rb")

The second call won't be run if the first fails.

You can wrap those in an if () to give you some flow-control:

if (
  system("VBoxManage createvm --name test1") && 
  system("ruby test.rb")
) 
  # do something
else
  # do something with $?
end

1 Comment

This is so far what I want to achieve, straightforward and clear enough. Thank you
6

Ruby 2.6 added option to raise exception in Kernel#system:

system("command", exception: true)

Comments

2

I want something like

system("VBoxManage createvm --name test1", 0) <-- where the second parameter checks the exit code and confirms that that system call was successful, and if not, it'll raise an error or do something of that sort.

You can add exception: true to your system call to have an error raised on non 0 exit codes.

For example, consider this small wrapper around system which prints the command (similar to bash -x, fails if there's a non 0 exit code (like bash -e) and returns the actual exit code:

def sys(cmd, *args, **kwargs)
  puts("\e[1m\e[33m#{cmd} #{args}\e[0m\e[22m")
  system(cmd, *args, exception: true, **kwargs)
  return $?.exitstatus
end

To be called like: sys("hg", "update") If you want to call a program that uses a different convention for exit codes, you can suppress raising the exception:

sys("robocopy", src, dst, "/COPYALL", "/E", "/R:0", "/DCOPY:T", exception: false)

You can also suppress stdout and stderr for noisy programs:

sys("hg", "update", "default", :out => File::NULL, :err => File::NULL)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.