Exit code of a compound command and errexit

Sometimes you may need to capture an exit code of a command while keeping the errexit option enabled during execution of that command.

|| rc=$? or wrapping the command in an if statement, does not work correctly for compound commands and functions as ||, if, while, etc. will completely disable the errexit option for that command, therefore failures in the middle of a function will not cause that function to fail.

Example:

set -euo pipefail

failing_fn () {
    return 5
}

complex_fn () {
    true
    failing_fn
    echo 'Should not see me'
}
rc=UNCHANGED
complex_fn || rc=$?
echo "$rc"

outputs

Should not see me
UNCHANGED

One could use the PIPESTATUS trick, like complex_fn | cat; rc=${PIPESTATUS[0]} but it does not work with -o pipefail

Here’s one of the ways to handle this:

set +e
(set -e; complex_fn)
rc=$?
set -e
echo "$rc"

outputs 5

This method also works with pipes and -o pipefail:

set +e
(set -e; true | complex_fn | true)
rc=$?
set -e
echo "$rc"

also outputs 5