'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.
Solution 1:[1]
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: Thelscommand fails inside your function, and, due to being the last command in the function, the function reportsls's nonzero exit code to the caller, your top-level script scope. In that scope, theERRtrap is in effect, and it is invoked (but note that execution will continue, unless you explicitly callexitfrom the trap).With
-e(but without-E): Thelscommand fails inside your function, and becauseset -eis in effect, Bash instantly exits, directly from the function scope - and since there is noERRtrap 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.
Solution 2:[2]
You need to use set -o errtrace for the function to inherit the trap.
Solution 3:[3]
Replace ERR with EXIT and it will work.
The syntax of the trap command is: trap [COMMANDS] [SIGNALS]
For more info, please read http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html
Solution 4:[4]
We have these options for debugging:
-eExit immediately on failure-EIf set, any trap on ERR is inherited by shell functions-uExit when there is an unbound variable-oGive a option-name to set- pipefail The return values of last (rightmost) command (exit code)
-vPrint all shell input lines as they are read-xPrint trace of commands
For handling the errors we can catch directory with trap
trap 'echo >&2 "Error - exited with status $? at line $LINENO' ERR
Or a better version ref :
trap 'echo >&2 "Error - exited with status $? at line $LINENO:";
pr -tn $0 | tail -n+$((LINENO - 3)) | head -n7' ERR
Or a function:
function __error_handing__(){
local last_status_code=$1;
local error_line_number=$2;
echo 1>&2 "Error - exited with status $last_status_code at line $error_line_number";
perl -slne 'if($.+5 >= $ln && $.-4 <= $ln){ $_="$. $_"; s/$ln/">" x length($ln)/eg; s/^\D+.*?$/\e[1;31m$&\e[0m/g; print}' -- -ln=$error_line_number $0
}
and call it this way:
trap '__error_handing__ $? $LINENO' ERR
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | chepner |
| Solution 3 | mklement0 |
| Solution 4 | Shakiba Moshiri |
