David Wolever
David Wolever

Reputation: 154464

Bash get exit status of command when 'set -e' is active?

I generally have -e set in my Bash scripts, but occasionally I would like to run a command and get the return value.

Without doing the set +e; some-command; res=$?; set -e dance, how can I do that?

Upvotes: 93

Views: 13455

Answers (6)

Lada00
Lada00

Reputation: 1

Another option is to use simple if. It is a bit longer, but fully supported by bash, i.e. that the command can return non-zero value, but the script doesn't exit even with set -e. See it in this simple script:

#! /bin/bash -eu

f () {
  return 2
}

if f;then
  echo Command succeeded
else
  echo Command failed, returned: $?
fi
echo Script still continues.

When we run it, we can see that script still continues after non-zero return code:

$ ./test.sh 
Command failed, returned: 2
Script still continues.

Upvotes: 0

reddot
reddot

Reputation: 985

Given behavior of shell described at this question it's possible to use following construct:

#!/bin/sh

set -e

{ custom_command; rc=$?; } || :

echo $rc

Upvotes: 1

Adrian Frühwirth
Adrian Frühwirth

Reputation: 45566

From the bash manual:

The shell does not exit if the command that fails is [...] part of any command executed in a && or || list [...].

So, just do:

#!/bin/bash

set -eu

foo() {
  # exit code will be 0, 1, or 2
  return $(( RANDOM % 3 ))
}

ret=0
foo || ret=$?
echo "foo() exited with: $ret"

Example runs:

$ ./foo.sh
foo() exited with: 1
$ ./foo.sh
foo() exited with: 0
$ ./foo.sh
foo() exited with: 2

This is the canonical way of doing it.

Upvotes: 123

konsolebox
konsolebox

Reputation: 75478

Use a wrapper function to execute your commands:

function __e {
    set +e
    "$@"
    __r=$?
    set -e
}

__e yourcommand arg1 arg2

And use $__r instead of $?:

if [[ __r -eq 0 ]]; then
    echo "success"
else
    echo "failed"
fi

Another method to call commands in a pipe, only that you have to quote the pipe. This does a safe eval.

function __p {
    set +e
    local __A=() __I
    for (( __I = 1; __I <= $#; ++__I )); do
        if [[ "${!__I}" == '|' ]]; then
            __A+=('|')
        else
            __A+=("\"\$$__I\"")
        fi
    done
    eval "${__A[@]}"
    __r=$?
    set -e
}

Example:

__p echo abc '|' grep abc

And I actually prefer this syntax:

__p echo abc :: grep abc

Which I could do with

...
        if [[ ${!__I} == '::' ]]; then
...

Upvotes: -3

Phil Miller
Phil Miller

Reputation: 38118

Maybe try running the commands in question in a subshell, like this?

res=$(some-command > /dev/null; echo $?)

Upvotes: 9

KitsuneYMG
KitsuneYMG

Reputation: 12901

as an alternative

ans=0
some-command || ans=$?

Upvotes: 17

Related Questions