5

I am using an ERR trap to catch any error in my bash script and output what happened to log. (similar to this question: Trap, ERR, and echoing the error line ) It works as expected. The only problem is, that at some point in my script an exitcode !=0 is expected to happen. How can I make the trap not trigger in this situation?

Here is some code:

err_report() {
    echo "errexit on line $(caller)" | tee -a $LOGFILE 1>&2
}

trap err_report ERR

Then later in the script:

<some command which occasionally will return a non-zero exit code>

if [ $? -eq 0 ]; then
    <handle stuff>
fi

Everytime the command returns non-zero my trap is triggered. Can I avoid this only for this part of the code?

I checked this question: Correct behavior of EXIT and ERR traps when using `set -eu` but I am not really getting how to apply it to my case - if at all aplicable.

masgo
  • 272

4 Answers4

6

An ERR trap will not trigger if an error code is immediately "caught", which means that you can use if statements and whatnot without having to flip error trapping on and off all the time. However, you cannot use checking $? for flow control, because as the time you get to that check, you already (may) have the uncaught error.

If you have a command you expect to fail -- and you do not want those failures to trigger the trap, you simply have to catch the failure. Wrapping them in an if statement is clunky and verbose, but this shorthand works nicely:

/bin/false || :  # will not trigger an ERR trap

However, if you want to do things when a command fails, if will be fine here:

if ! /bin/false; then
    echo "this was not caught by the trap!"
fi

Or alternatively, else will catch the error state also:

if /bin/false; then
    : # dead code
else
    echo "this was not caught by the trap!"
fi

In sum, set -e and trap "command" ERR only get tripped if there is an error condition which is not immediately and intrinsically accounted for.

DopeGhoti
  • 76,081
  • Thanks for the reply. The "problem" is that the command above is not a simple command but rather a command chain with pipes which already takes up several lines. Is there a nice way to put this as if condition? – masgo May 31 '18 at 22:48
  • 2
    Just as with one command: if ! foo | bar | baz | quux; then stuff; fi. – DopeGhoti Jun 01 '18 at 15:59
  • I'd buy you lunch, but all I can do is upvote. – Chaim Eliyah Feb 26 '21 at 21:53
  • I appreciate the thought of the former, but will happily accept the latter (: – DopeGhoti Mar 01 '21 at 14:09
  • What's the expected behavior when set +e is used? Is the expectation that the script ignores the error and continues to run, but trap ERR is nevertheless triggered? – Sida Zhou Mar 26 '22 at 02:22
  • set +e is the opposite of set -e. set -e will cause the script to bail on any uncaught error regardless of if a trap is set. set +e will allow a script to continue, though a trap set to ERR should still fire irrespective to how the e switch is toggled. – DopeGhoti Mar 28 '22 at 14:14
3

You can enable/disable the ERR trap during portions of you code as necessary.

#!/bin/bash
err_report() {
    echo "errexit on line $(caller)"
}
trap err_report ERR
trap - ERR             # disable ERR trap
false
if [ $? -eq 0 ]; then
    printf "OK\n"
else
    printf "FAIL\n"    # prints FAIL
fi
trap err_report ERR    # enable ERR trap
false                  # prints errexit on line 14
JRFerguson
  • 14,740
3

I find that I often want to avoid ERR trap but I don't want to discard the return code of my function either. So the pattern I use is this:

RC=0
some_function || RC=$?

Then I do whatever I need to do with RC.

  • Brilliant! This can also be applied to subshells, e.g. OUTPUT="$(some_function)" || RC="$?". Note that the assignment should be done outside the subshell, as its variables cannot be read from outside. – Steen Schütt Jan 15 '24 at 17:59
2

The ERR trap obeys the same rules as set -e, that is, it doesn't take effect on commands that are used as conditions. So,

trap "echo error" ERR
false                     # this should trigger the trap
if ! false; then          # this shouldn't
     echo handle stuff
fi

Remember that the command in the if condition can be any command, it doesn't have to be [ .. ]. So, if you only want a true/false evaluation of the exit status of a command, just use it directly in the if condition.

If you need to save the exit code and avoid the ERR trap, you'll need to do something like

somecmd && :; ret=$?

Here, the && will squash the ERR trap, but since it only runs if the exit code is zero, we'll know the exit code is the same after :.

You may want to check BashFAQ 105: Why doesn't set -e (or set -o errexit, or trap ERR) do what I expected?

ilkkachu
  • 138,973