0

I am using Ruby to call a shell script via:

%x[ /root/script.sh -k -m -l -w -p]
exitCode = %x[echo $?]

if exitCode != 0
  do something
else
  do something else
end

My problem is the exit code is always equal to 0 even when I force the script to fail. How do I properly get the exit code of the script?

EDIT: Ok came in this morning and started digging through the script as I could not get an error code of anything besides 0. The script looks something like this...

failed() {
      if [ "$1" -ne 0 ] ; then
          echo "$2 failed. INSTALLATION FAILED! Exiting.";
      exit 1;
    fi
}

{

function 1
function 2..
function ..20

} | tee -a logFile.log 

So the last thing that the script runs is always this log file meaning I never get the real exit code.

  • Related: [Calling shell commands from Ruby](https://stackoverflow.com/questions/2232/calling-shell-commands-from-ruby). You can do `exitCode = $?.exitstatus` – Mark Plotnick Aug 15 '17 at 19:24
  • I just tried that, I got a TypeError no implicit conversion of Integer into String. So I tried doing exitCode = $?.exitstatus.to_i I am still getting the same error though – InsertNameHere Aug 15 '17 at 19:33
  • Interesting. Does `ruby -e '%x[/root/script.sh -k -m -l -w -p]; exitCode=$?.exitstatus; print exitCode'` fail, too? – Mark Plotnick Aug 15 '17 at 19:43
  • You two are on the wrong track. With backticks, the OP's current code contains a String (probably `0`), not an Integer exit status. Call #class on it and see. – Todd A. Jacobs Aug 15 '17 at 19:45
  • Oh, my intent was to suggest that `exitCode = %x[echo $?]` should be deleted and `exitCode = $?.exitstatus` should be added. – Mark Plotnick Aug 15 '17 at 19:53
  • Possible duplicate of [ruby system command check exit code](https://stackoverflow.com/questions/18728069/ruby-system-command-check-exit-code) – Brad Werth Aug 15 '17 at 20:33
  • Maybe I should of noted this, I didn't think it mattered but this code is being run through chef. I tried the duplicate answer, even when it fails I still get a true and returns 0 for $? and 0 for $?.exitstatus. I am begining to think it may have to do with the script it is over 3,000 lines long and I did not write it. I skimmed through it and it should have an exit code of 1 when it fails but I am left wondering now after trying all these different ways without success. – InsertNameHere Aug 15 '17 at 20:53
  • In your Ruby code, you create two separate (unrelated) processes one after the other. The first process runs `script.sh`, collects the output, and throws it away (and you better had used `system` with a redirection to `/dev/null` instead of `%x`. The second process consists of a simple `echo $?`. This is the output you are catching. Since `$?` is always 0 when a shell script starts to run, you always get the string '0' in return. – user1934428 Aug 16 '17 at 06:52

1 Answers1

2

Echo is True; Inspect Your Last Process Instead

What's Wrong

Your current code will almost always evaluate its if-statement as true because /bin/echo or the shell builtin echo return success unless standard output is closed or some other error occurs. Consider the following Bash snippet:

echo `false`; echo $? # 0
echo >&-; echo $?     # 1

In addition, in your current code, exitCode is a String, not an Integer. It's being assigned the standard output of your echo command, so you'd have to call Kernel#Integer or String#to_i on it to cast the variable before attempting a valid comparison. For example, consider the following Ruby:

`echo $?`.class #=> String
"0" == 0        #=> false
"0".to_i == 0   #=> true

How to Fix the General Case

You need to test the exit status directly, or inspect the captured output. For example, in Ruby, you can test the last status of the /bin/false command with:

captured_output = `/bin/false`
$?.exitstatus
#=> 1

Fixing Your Specific Example

If you didn't understand anything above, just fix your code by stripping out all the non-essentials. Based on your example, you don't need the interim variable, nor do you actually need to store standard output. Unless you're evaluating specific non-zero exit statuses, you don't even need to inspect the process status directly or even use an equality statement.

Follow the KISS principle, and just evaluate the truthiness of the Kernel#system call. For example:

# Just call system if you don't care about the output.
# The result will be true, false, or nil.
if system('/root/script.sh -k -m -l -w -p')
  'clean exit'
else
  "non-zero exit: #{$?}"
end
Todd A. Jacobs
  • 81,402
  • 15
  • 141
  • 199
  • I still can't seem to get this to work %x[ /root/script.sh -k -m -l -w -p] captured_output = 'false' puts $?.exitstatus It still always come back as 0. – InsertNameHere Aug 15 '17 at 20:32
  • 1
    You should **not** use `%x`. Look at the code which CodeGnome provided. The only variation to his code which I would propose, is to do `system('/root/script.sh -k -m -l -w -p >dev/null')`, because you are not interested in the actual standard output from the script being executed. – user1934428 Aug 16 '17 at 06:56