Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to trap ERR when using 'set -e' in Bash

I have a simple script :

#!/bin/bash set -e trap "echo BOO!" ERR   function func(){     ls /root/ }  func 

I would like to trap ERR if my script fails (as it will here b/c I do not have the permissions to look into /root). However, when using set -e it is not trapped. Without set -e ERR is trapped.

According to the bash man page, for set -e :

... A trap on ERR, if set, is executed before the shell exits. ...

Why isn't my trap executed? From the man page it seems like it should.

like image 553
irritable_phd_syndrome Avatar asked Mar 04 '16 15:03

irritable_phd_syndrome


People also ask

What does [- Z $1 mean in bash?

$1 means an input argument and -z means non-defined or empty. You're testing whether an input argument to the script was defined when running the script.

What happens if you use set in a bash script?

In Bash, the set command allows you to manage certain flags and characteristics to influence how your bash scripts behave. These controls ensure that your scripts follow the correct path and that Bash's peculiar behavior does not cause difficulties.

What is set +E in bash?

Set –e is used within the Bash to stop execution instantly as a query exits while having a non-zero status. This function is also used when you need to know the error location in the running code.


1 Answers

chepner's answer is the best solution: If you want to combine set -e (same as: set -o errexit) with an ERR trap, also use set -o errtrace (same as: set -E).

In short: use set -eE in lieu of just set -e:

#!/bin/bash  set -eE  # same as: `set -o errexit -o errtrace` trap 'echo BOO!' ERR   function func(){   ls /root/ }  # Thanks to -E / -o errtrace, this still triggers the trap,  # even though the failure occurs *inside the function*. func  

A more sophisticated example trap example that prints the message in red and also prints the exit code:
trap 'printf "\e[31m%s: %s\e[m\n" "BOO!" $?' ERR


man bash says about set -o errtrace / set -E:

If set, any trap on ERR is inherited by shell functions, command substitutions, and commands executed in a subshell environment. The ERR trap is normally not inherited in such cases.

What I believe is happening:

  • Without -e: The ls command fails inside your function, and, due to being the last command in the function, the function reports ls's nonzero exit code to the caller, your top-level script scope. In that scope, the ERR trap is in effect, and it is invoked (but note that execution will continue, unless you explicitly call exit from the trap).

  • With -e (but without -E): The ls command fails inside your function, and because set -e is in effect, Bash instantly exits, directly from the function scope - and since there is no ERR trap in effect there (because it wasn't inherited from the parent scope), your trap is not called.

While the man page is not incorrect, I agree that this behavior is not exactly obvious - you have to infer it.

like image 147
mklement0 Avatar answered Sep 22 '22 22:09

mklement0