在 Bash 中使用 'set -e' 时如何捕获 ERR
How to trap ERR when using 'set -e' in Bash
我有一个简单的脚本:
#!/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
:
... A trap on ERR, if set, is executed before the shell exits. ...
为什么我的陷阱没有被执行?从手册页看来应该如此。
将 ERR
替换为 EXIT
即可。
trap
命令的语法是:trap [COMMANDS] [SIGNALS]
更多信息,请阅读http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html
是最好的方案 :如果要结合set -e
(同:set -o errexit
)使用 ERR
陷阱,也使用 set -o errtrace
(等同于: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
:
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.
我认为正在发生的事情:
Without -e
:ls
命令在您的函数中失败,并且由于是函数中的最后一个命令,该函数向调用者(您的顶级脚本范围)报告 ls
的非零退出代码。在那个作用域中,ERR
陷阱生效,并被调用(但请注意,执行将继续,除非你明确地从陷阱中调用exit
) .
有-e
(但没有-E
):ls
命令在里面失败你的函数,因为set -e
生效,Bash立即退出,直接从函数范围 - 因为没有 ERR
陷阱生效 那里 (因为它不是从父范围继承的),你的陷阱不会被调用。
虽然 man
页面没有错误,但我同意这种行为并不十分明显 - 您必须进行推断。
继承陷阱的功能需要使用set -o errtrace
我们有这些调试选项:
-e
失败立即退出
-E
如果设置,ERR 上的任何陷阱都会被 shell 函数继承
-u
有未绑定变量时退出
-o
给个option-name设置
- pipefail 最后一个(最右边)命令(退出代码)的 return 值
-v
在读取时打印所有 shell 输入行
-x
打印命令跟踪
为了处理错误,我们可以使用 trap
捕获目录
trap 'echo >&2 "Error - exited with status $? at line $LINENO' ERR
或更好的版本ref :
trap 'echo >&2 "Error - exited with status $? at line $LINENO:";
pr -tn [=11=] | tail -n+$((LINENO - 3)) | head -n7' ERR
或者一个函数:
function __error_handing__(){
local last_status_code=;
local error_line_number=;
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 [=12=]
}
并这样称呼它:
trap '__error_handing__ $? $LINENO' 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
:
... A trap on ERR, if set, is executed before the shell exits. ...
为什么我的陷阱没有被执行?从手册页看来应该如此。
将 ERR
替换为 EXIT
即可。
trap
命令的语法是:trap [COMMANDS] [SIGNALS]
更多信息,请阅读http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html
set -e
(同:set -o errexit
)使用 ERR
陷阱,也使用 set -o errtrace
(等同于: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
:
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.
我认为正在发生的事情:
Without
-e
:ls
命令在您的函数中失败,并且由于是函数中的最后一个命令,该函数向调用者(您的顶级脚本范围)报告ls
的非零退出代码。在那个作用域中,ERR
陷阱生效,并被调用(但请注意,执行将继续,除非你明确地从陷阱中调用exit
) .有
-e
(但没有-E
):ls
命令在里面失败你的函数,因为set -e
生效,Bash立即退出,直接从函数范围 - 因为没有ERR
陷阱生效 那里 (因为它不是从父范围继承的),你的陷阱不会被调用。
虽然 man
页面没有错误,但我同意这种行为并不十分明显 - 您必须进行推断。
继承陷阱的功能需要使用set -o errtrace
我们有这些调试选项:
-e
失败立即退出-E
如果设置,ERR 上的任何陷阱都会被 shell 函数继承-u
有未绑定变量时退出-o
给个option-name设置- pipefail 最后一个(最右边)命令(退出代码)的 return 值
-v
在读取时打印所有 shell 输入行-x
打印命令跟踪
为了处理错误,我们可以使用 trap
捕获目录trap 'echo >&2 "Error - exited with status $? at line $LINENO' ERR
或更好的版本ref :
trap 'echo >&2 "Error - exited with status $? at line $LINENO:";
pr -tn [=11=] | tail -n+$((LINENO - 3)) | head -n7' ERR
或者一个函数:
function __error_handing__(){
local last_status_code=;
local error_line_number=;
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 [=12=]
}
并这样称呼它:
trap '__error_handing__ $? $LINENO' ERR