9nonnatus
9nonnatus

Reputation: 152

Continue after failed command in bash script with `set -e`

I'm writing a script that parses Bundler's output for errors, but I've run into an issue that I'm not certain how to handle:

response="$(bundle install 2>&1)"
echo $response

If Bundler fails, then $response is blank, and I can't parse it as I need. I've tried:

response="$(bundle install 2>&1 || true)"
echo $response

Which works, but isn't what I want because I still want my script to fail when Bundler fails.

How can I allow bundle install to fail and still read its output?

I should also add that I'm using set -e, which I need.

Edit:

After reading PSkocik's answer, this allows me to continue through the error, while still exiting when it happens:

response="$(bundle install 2>&1)" || bundle_status=$?
# Continue working (In my case, I'm parsing the errors to give more
# meaningful output for the context in which bundler is being run).
# And now I can exit with the given status code, or default to 0.
exit ${bundle_status:-0}

Upvotes: 5

Views: 3745

Answers (3)

Petr Skocik
Petr Skocik

Reputation: 60068

It's because of the -e option. You need to make sure the error return status gets tested for:

man sh on -e (man bash is a little more verbose on this, but in the same spirit)

If not interactive, exit immediately if any untested command fails. The exit status of a command is considered to be explicitly tested if the command is used to control an if, elif, while, or until; or if the command is the left hand operand of an “&&” or “||” operator.

Here's an example with a bundler mock (in sh, but should work in bash too):

#!/bin/sh
set -e
bundle(){
  echo "i'm errorring here" >&2
  return 32
}

response="$(bundle 2>&1)"
echo "won't get here if -e is set"

You can solve it like this:

response="$(bundle 2>&1)" || ret=$? #`set -e` won't kill us now
echo "will get here now"
echo "the bundle command exited with $ret"
echo "the response is ${response}"

If you need to echo strings that aren't yours, you should consider printf instead of echo (and don't forget double quotes): https://unix.stackexchange.com/questions/65803/why-is-printf-better-than-echo

Upvotes: 4

chepner
chepner

Reputation: 531205

The problem is, you don't want your script to fail when Bundler does. You just want to exit after you've caught the error and done something with the error message. Don't use set -e.

if ! response=$(bundle install 2>&1); then
    ret=$?
    # do something with $reponse
    exit $ret
fi

Upvotes: 0

betterworld
betterworld

Reputation: 264

One option is to get rid of -e and find a way to do more sophisticated error handling.

The other option is to replace the bundle command with a wrapper. Example:

function my-bundle {
    set +e
    output=$(bundle "$@" 2>&1)
    ret=$?
    echo "$output" # Add code for output parsing here
    set -e
    return $ret
}

# main script
set -e

There is no way you can have set -e handle the return status of a command and run code after the command. (Unless you want to use trap)

Upvotes: 1

Related Questions