I agree with the consensus here - generally just don't use set -e
.
Consider maybe a trap instead, like this.
Second, most bash checks against a condition will "consume" the "exception". I'm sure there's better terminology, but test it for yourself; if you check for a fail immediately, things like set -e
or a trap
won't usually see the fail, and you can handle it explicitly.
user1934428's solution does this with a double-pipe to explicitly check the exit status and set the value on a fail.
Be careful - even testing only for success is likely to have this effect. See below.
$: cat tst
#! bash
set -e
cmd=( bogus command )
# check fail only (#1), success only (#2), both (#3)
"${cmd[@]}" || echo "yep, failed #1"
"${cmd[@]}" && echo "worked fine! #2" # doesn't print
"${cmd[@]}" && echo "worked fine!" || echo "oops! #3"
if "${cmd[@]}" # the if is a test, "catches" the fail
then echo "won't get here"
else rc=$?
echo "<${cmd[@]}> returned with exit code: $rc - #4"
fi
# running it without checking here, #5
"${cmd[@]}" # uncaught, set -e kills the script here
echo "This doesn't print! #5"
The cmd
array is just an easy way to make sure the echo
doesn't have to be separately maintained.
Running it looks like this:
$: ./tst
./tst: line 5: bogus: command not found
yep, failed #1
./tst: line 6: bogus: command not found
./tst: line 7: bogus: command not found
oops! #3
./tst: line 8: bogus: command not found
<bogus command> returned with exit code: 127 - #4
./tst: line 14: bogus: command not found
Hope that helps.
addendum
We totally did NOT address the output capture, but you seem to have that; and I completely agree that it's probably better to avoid the "not" (!
).