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

我有个简单的剧本:

#!/bin/bash
set -e
trap "echo BOO!" ERR


function func(){
ls /root/
}


func

如果我的脚本失败了,我想捕获 ERR (因为它将在这里 b/c 我没有权限查看/root)。但是,当使用 set -e时,它不会被捕获。没有 set -e,ERR 就被困住了。

根据 bash 手册页面,对于 set -e:

... 如果设置了 ERR 陷阱,则在 shell 退出之前执行。 ..。

- 为什么我的陷阱没有被执行?-从手册上看应该是这样的。

29378 次浏览

EXIT代替 ERR,它就会工作。

trap命令的语法是: trap [COMMANDS] [SIGNALS]

欲了解更多信息,请阅读 http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html

Chepner 的回答 是最好的解决方案 : 如果你想要 将 ABC0(与 ABC1相同)与 ABC2陷阱结合,也可以使用 ABC3(与 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

一个更复杂的例子 trap打印了红色的消息,同时也打印了退出代码:
trap 'printf "\e[31m%s: %s\e[m\n" "BOO!" $?' ERR


man bash是关于 set -o errtrace/set -E的:

如果设置了,ERR 上的任何陷阱都将由子 shell 环境中执行的 shell 函数、命令替换和命令继承。在这种情况下,通常不会继承 ERR 陷阱。

我相信正在发生的事情:

  • 没有 -e: 函数内部的 ls命令失败,并且,由于是函数中的最后一个命令,函数将 ls的非零退出代码报告给调用者,即您的顶级脚本作用域。在 那个望远镜中,ERR陷阱生效并被调用(但请注意,除非从陷阱中显式调用 exit,否则执行将继续)。

  • 使用 -e(但没有 -E) : ls命令失败 在你的职能范围内,并且因为 set -e有效,Bash 马上退出,直接从函数范围-并且因为没有 ERR陷阱有效 那里(因为它不是从父范围继承的) ,所以不调用您的陷阱。

虽然 man页面并不是不正确的,但我同意这种行为并不完全明显——你必须推断出来。

对于继承陷阱的函数,需要使用 set -o errtrace

我们有这些调试选项:

  • -e故障后立即退出
  • 如果设置了,ERR 上的任何陷阱都由 shell 函数继承
  • 有未绑定变量时退出
  • 给出一个要设置的选项名
    • Last (最右边)命令的返回值(退出代码)
  • 当所有 shell 输入行被读取时打印它们
  • 打印命令跟踪

为了处理错误,我们可以用 陷阱捕获目录

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

或者更好的版本 裁判:

trap 'echo >&2 "Error - exited with status $? at line $LINENO:";
pr -tn $0 | tail -n+$((LINENO - 3)) | head -n7' ERR

或者功能:

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
}

这么说吧:

trap  '__error_handing__ $? $LINENO' ERR