在想出一种方法来捕捉我的Bash脚本中的错误的过程中,我一直在试验“set -e”,“set -E”和“trap”命令。在这个过程中,我发现了一些奇怪的行为,如何在函数的上下文中评估$LINENO
。首先,这是我正在尝试记录错误的简化版本:
#!/bin/bash
set -E
trap 'echo Failed on line: $LINENO at command: $BASH_COMMAND && exit $?' ERR
现在,根据故障发生的位置,行为会有所不同。例如,如果我按照上述内容执行:
echo "Should fail at: $((LINENO + 1))"
false
我得到以下输出:
Should fail at: 6
Failed on line: 6 at command: false
一切都如预期。第6行是包含单个命令“false”的行。但是,如果我将失败的命令包装在一个函数中并像这样调用它:
function failure {
echo "Should fail at $((LINENO + 1))"
false
}
failure
然后我得到以下输出:
Should fail at 7
Failed on line: 5 at command: false
如您所见,$BASH_COMMAND
包含正确的失败命令:“false”,但$LINENO
将“失败”函数定义的第一行报告为当前命令。这对我来说毫无意义。有没有办法获得$BASH_COMMAND
中引用的行的行号?
这种行为可能特定于旧版本的Bash。我暂时停留在3.2.51。如果在以后的版本中行为发生了变化,那么知道是否有一种解决方法可以获得我想要的值3.2.51仍然是一件好事。
编辑:我害怕有些人感到困惑,因为我把我的例子分成了几块。让我试着澄清一下我拥有什么,得到什么,以及我想要什么。
这是我的剧本:
#!/bin/bash
set -E
function handle_error {
local retval=$?
local line=$1
echo "Failed at $line: $BASH_COMMAND"
exit $retval
}
trap 'handle_error $LINENO' ERR
function fail {
echo "I expect the next line to be the failing line: $((LINENO + 1))"
command_that_fails
}
fail
现在,我期望的是以下输出:
I expect the next line to be the failing line: 14
Failed at 14: command_that_fails
现在,我获取的是以下输出:
I expect the next line to be the failing line: 14
Failed at 12: command_that_fails
但是第12行不是 command_that_fails
。第12行是function fail {
,这有点不太有帮助。我还检查了${BASH_LINENO[@]}
数组,不有第14行的条目。
答案 0 :(得分:7)
对于4.1之前的bash版本,需要一个特殊级别的可怕,hacky,性能破坏地狱来解决一个问题,其中,在出现错误时,系统会在调用错误处理程序之前跳回到函数定义点。 / p>
#!/bin/bash
set -E
set -o functrace
function handle_error {
local retval=$?
local line=${last_lineno:-$1}
echo "Failed at $line: $BASH_COMMAND"
echo "Trace: " "$@"
exit $retval
}
if (( ${BASH_VERSION%%.*} <= 3 )) || [[ ${BASH_VERSION%.*} = 4.0 ]]; then
trap '[[ $FUNCNAME = handle_error ]] || { last_lineno=$real_lineno; real_lineno=$LINENO; }' DEBUG
fi
trap 'handle_error $LINENO ${BASH_LINENO[@]}' ERR
fail() {
echo "I expect the next line to be the failing line: $((LINENO + 1))"
command_that_fails
}
fail
答案 1 :(得分:5)
BASH_LINENO
是一个数组。您可以在其中引用不同的值:${BASH_LINENO[1]}
,${BASH_LINENO[2]}
等来备份堆栈。 (此数组中的位置与BASH_SOURCE
数组中的位置对齐,如果您想获得想象并实际打印堆栈跟踪)。
但更好的是,您可以在陷阱中注入正确的行号:
failure() {
local lineno=$1
echo "Failed at $lineno"
}
trap 'failure ${LINENO}' ERR
您可能还会在https://stackoverflow.com/a/185900/14122处找到我之前的答案(有一个更完整的错误处理示例)。
答案 2 :(得分:-1)
这种行为非常合理。
调用堆栈的整个画面可在发生错误时提供全面的信息。你的例子证明了一个很好的错误信息;你可以看到错误实际发生的位置以及触发函数的行等等。
如果解释器/编译器无法准确指出错误实际发生的位置,则可能更容易混淆。