简体   繁体   中英

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.

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.

您需要对函数使用set -o errtrace来继承陷阱。

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

We have these options for debugging:

  • -e Exit immediately on failure
  • -E If set, any trap on ERR is inherited by shell functions
  • -u Exit when there is an unbound variable
  • -o Give a option-name to set
    • pipefail The return values of last (rightmost) command (exit code)
  • -v Print all shell input lines as they are read
  • -x Print 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM