简体   繁体   English

在 Bash 中使用“set -e”时如何捕获 ERR

[英]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).如果我的脚本失败,我想捕获 ERR(因为它会在这里 b/c 我没有查看 /root 的权限)。 However, when using set -e it is not trapped.但是,当使用set -e时,它不会被困住。 Without set -e ERR is trapped.没有set -e ERR 被困。

According to the bash man page, for set -e :根据 bash 手册页,对于set -e

... A trap on ERR, if set, is executed before the shell exits. ...如果设置了 ERR 陷阱,则会在 shell 退出之前执行。 ... ...

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 ) . chepner 的答案是最好的解决方案:如果要将set -e (与set -o errexit相同)与ERR陷阱结合使用,还可以使用set -o errtrace (与set -E相同)

In short: use set -eE in lieu of just set -e :简而言之:使用set -eE代替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示例,以红色打印消息并打印退出代码:
trap 'printf "\\e[31m%s: %s\\e[m\\n" "BOO!" $?' ERR


man bash says about set -o errtrace / set -E : man bash关于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.如果设置,则 ERR 上的任何陷阱都由 shell 函数、命令替换和在子 shell 环境中执行的命令继承。 The ERR trap is normally not inherited in such cases.在这种情况下,ERR 陷阱通常不会被继承。

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.没有-els命令在您的函数内失败,并且由于是函数中的最后一个命令,该函数将ls的非零退出代码报告给调用者,即您的顶级脚本范围。 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).该范围内ERR陷阱生效并被调用(但请注意,执行将继续,除非您显式地从陷阱中调用exit )。

  • 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.随着-e (但没有-E )的ls命令自己的函数中失败,因为set -e生效,猛砸立即退出,直接从功能范围-而因为没有ERR陷阱效应存在(因为它不是从父作用域继承的),则不会调用您的陷阱。

While the man page is not incorrect, I agree that this behavior is not exactly obvious - you have to infer it.虽然man页并没有错,但我同意这种行为并不完全明显 - 您必须推断它。

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

Replace ERR with EXIT and it will work.EXIT替换ERR它将起作用。

The syntax of the trap command is: trap [COMMANDS] [SIGNALS] trap命令的语法是: trap [COMMANDS] [SIGNALS]

For more info, please read http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html有关更多信息,请阅读http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html

We have these options for debugging:我们有这些调试选项:

  • -e Exit immediately on failure -e失败立即退出
  • -E If set, any trap on ERR is inherited by shell functions -E如果设置,ERR 上的任何陷阱都由 shell 函数继承
  • -u Exit when there is an unbound variable -u存在未绑定变量时退出
  • -o Give a option-name to set -o给出要设置的选项名称
    • pipefail The return values of last (rightmost) command (exit code) pipefail 最后(最右边)命令的返回值(退出代码)
  • -v Print all shell input lines as they are read -v在读取时打印所有 shell 输入行
  • -x Print trace of commands -x打印命令跟踪

For handling the errors we can catch directory with trap为了处理错误,我们可以使用trap捕获目录

trap 'echo >&2 "Error - exited with status $? at line $LINENO' ERR

Or a better version ref :或者更好的版本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:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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