Seb T
Seb T

Reputation: 912

Reliable exit from bash function on error

So I learned that set -e has no effect inside Bash functions and one needs to combine set -o errexit with a trap to automatically exit from within a function in case an error occurs. Unfortunately this does not seem to work if a function is called within an if statement or when combined with && or ||.

Let's say I set up error handling like this:

#!/bin/bash
set -e
set -o errtrace
trap 'rc=$?; echo "ERROR catched."; exit $rc' ERR

function myfunc() {
  bad_command
  echo "SUCCESS!"
}

I get the following desired results:

myfunc
test.sh: line 7: bad_command: command not found
ERROR catched.

myfunc | cat
test.sh: line 7: bad_command: command not found
ERROR catched.

a=$(myfunc)
test.sh: line 7: bad_command: command not found
ERROR catched.

and these undesired results:

myfunc || true
test.sh: line 7: bad_command: command not found
SUCCESS!

myfunc && true
test.sh: line 7: bad_command: command not found
SUCCESS!

if myfunc; then true; fi
test.sh: line 7: bad_command: command not found
SUCCESS!

How can I ensure that execution within a function is always aborted ow matter how the function is executed?

Upvotes: 3

Views: 153

Answers (1)

Paul Hodges
Paul Hodges

Reputation: 15418

c.f. the trap documentation, specifically -

If a sigspec is ERR, the command arg is executed whenever a simple command has a non-zero exit status, subject to the following conditions. The ERR trap is not executed if the failed command is part of the command list immediately following a while or until keyword, part of the test in an if statement, part of a && or || list, or if the command's return value is being inverted via !.

You were on the right track with errtrace, but ERR traps are ignored when there's already a condition test on the command.

From the GNU Bash Manual Functions page -

All other aspects of the shell execution environment are identical between a function and its caller with these exceptions: the DEBUG and RETURN traps are not inherited unless the function has been given the trace attribute using the declare builtin or the -o functrace option has been enabled with the set builtin, (in which case all functions inherit the DEBUG and RETURN traps), and the ERR trap is not inherited unless the -o errtrace shell option has been enabled. See Bourne Shell Builtins, for the description of the trap builtin.

So you can bypass this with functrace and manually checking with the DEBUG option if you want.

set -o functrace
trap 'rc=$?; if ((rc)) then echo "ERROR $rc caught."; exit $rc; fi' debug

Skip the set -e, but understand exit is aborting the script, not just returning from the function.

Upvotes: 2

Related Questions