我正在玩bash绕过这个夏天下午的炎热,突然间我有一个神秘的结果,我无法确定它的起源。
让我稍微解释一下。
我正在使用陷阱ERR来为我的bash脚本创建一些调试功能。
这是运行良好的脚本:
traperror () {
local err=$? # error status
local line=$1 # LINENO
[ "$2" != "" ] && local funcstack=$2 # funcname
[ "$3" != "" ] && local linecallfunc=$3 # line where func was called
echo "<---"
echo "ERROR: line $line - command exited with status: $err"
if [ "$funcstack" != "" ]; then
echo -n " ... Error at function ${funcstack[0]}() "
if [ "$linecallfunc" != "" ]; then
echo -n "called at line $3"
fi
echo
fi
echo "--->"
}
#trap 'traperror $LINENO ${FUNCNAME}' ERR
somefunction () {
trap 'traperror $LINENO ${FUNCNAME} $BASH_LINENO' ERR
asdfas
}
somefunction
echo foo
输出为(stderr为/dev/null
为清晰; bash错误当然是foo.sh: line 23: asdfas: command not found
,如您所知错误代码127)
~$ bash foo.sh 2> /dev/null
<---
ERROR: line 21 - command exited with status: 127
... Error at function somefunction() called at line 24
--->
foo
所有行号都是正确,第21行是启动函数“somefunction”的地方,第24行是调用它的地方。
然而如果我取消注释第一个陷阱(主要的陷阱),我会得到此输出:
~$ bash foo.sh 2> /dev/null
<---
ERROR: line 21 - command exited with status: 127
... Error at function somefunction() called at line 24
--->
<---
ERROR: line 15 - command exited with status: 127
--->
foo
如果我取消注释第一个陷阱并注释第二个陷阱,我得到错误在第23行,这也是正确的,因为它是放置错误命令的绝对行。
~$ bash foo.sh
<---
ERROR: line 23 - command exited with status: 127
--->
foo
所以我的问题是:为什么第15行呢?该行号来自哪里?第15行是陷阱功能的最后一行。任何人都可以用简单的英语解释为什么陷阱返回它所调用的函数的最后一行作为第21行产生错误的行?
提前致谢!
修改
以防万一有人对调试功能感兴趣。这是生产版本:
# Copyright (c): Hilario J. Montoliu <hmontoliu@gmail.com>
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version. See http://www.gnu.org/copyleft/gpl.html for
# the full text of the license.
set -o errtrace
trap 'traperror $? $LINENO $BASH_LINENO "$BASH_COMMAND" $(printf "::%s" ${FUNCNAME[@]})' ERR
traperror () {
local err=$1 # error status
local line=$2 # LINENO
local linecallfunc=$3
local command="$4"
local funcstack="$5"
echo "<---"
echo "ERROR: line $line - command '$command' exited with status: $err"
if [ "$funcstack" != "::" ]; then
echo -n " ... Error at ${funcstack} "
if [ "$linecallfunc" != "" ]; then
echo -n "called at line $linecallfunc"
fi
else
echo -n " ... internal debug info from function ${FUNCNAME} (line $linecallfunc)"
fi
echo
echo "--->"
}
somefunction () {
asdfasdf param1
}
somefunction
echo foo
哪个将起作用:
~$ bash foo.sh 2> /dev/null
<---
ERROR: line 26 - command 'asdfasdf param1' exited with status: 127
... Error at ::somefunction::main called at line 29
--->
<---
ERROR: line 22 - command 'asdfasdf param1' exited with status: 127
... internal debug info from function traperror (line 0)
--->
foo
答案 0 :(得分:6)
一些相关事实/背景信息:
ERR
上的陷阱不会被shell函数继承,即使它们获得了其余的环境,除非设置了errtrace
。
函数的退出状态是其最后一个命令的退出状态。
我猜测发生了什么:
如果两个陷阱都处于活动状态,
ERR
陷阱。 LINENO
是不存在的命令。ERR
陷阱。 LINENO
仍设置为traperror
的最后一行,因为它是最后一行,仍然是当前行,因为尚未执行任何新行。如果只有shell陷阱处于活动状态(函数中的那个被注释掉)
ERR
陷阱触发。出于同样的原因,LINENO
是函数的最后一行,因为它是最后一行,仍然是当前行。答案 1 :(得分:2)
要确保在traperror
函数的第一个版本中ERR
信号处理程序不会执行两次,您可以忽略或重置ERR
信号处理程序为其默认操作对于你的程序的其余部分 - 在ERR
信号处理程序本身的定义内。这也应该始终为自定义EXIT
信号处理程序完成。
trap "" EXIT ERR # ignore
trap - EXIT ERR # reset
# for the first version of your traperror function
- trap 'traperror $LINENO ${FUNCNAME}' ERR
- trap 'traperror $LINENO ${FUNCNAME} $BASH_LINENO' ERR
+ trap 'traperror $LINENO ${FUNCNAME}; trap - ERR' ERR
+ trap 'traperror $LINENO ${FUNCNAME} $BASH_LINENO; trap - ERR' ERR