我正在编写一些shell函数,这些函数允许在发生错误时打印堆栈跟踪。为此,我使用了BASH_LINENO
数组,其中包含每一帧的行号。然后,我使用BASH_SOURCE
数组和line="$(tail -n+$lineno "$file" | head -n1)"
之类的子进程从文件中检索行。
无论如何,它工作得很好,除非eval
中发生错误。问题在于,行号与 之后的行相对应,该行已扩展给eval
的表达式。因此,当我检索带有头部和尾部的行时,很显然现在是错误的行,或者根本就不是行(lineno优于文件中的行数)。
所以我想知道如何获得实际的扩展行。我查看了Bash提供的变量,但在这种情况下似乎没有帮助。
示例script1.sh:
#!/usr/bin/env bash
eval "$(./script2.sh)"
script2.sh:
#!/usr/bin/env bash
echo
echo
echo
echo false
当我执行false
时碰到script1.sh
行时,我得到的行号是4,而我得到的文件源是script1.sh
,所以是错误的。
当该行不在文件中时,我可以检测到它,并打印第一条先前的eval行,但是它很hacky,并且我确定有几种情况需要处理。而且如果该行在文件中,那么我什至不知道它是否正确。
eval
是地狱:'(
理想情况下,BASH_COMMAND
也是一个数组,我可以从中检索命令,而不必读取文件。
我仅有的另一种想法是迫使用户将表达式的结果通过管道传递到将其压缩到一行的命令中。有什么想法或程序可以做到这一点吗?一个简单的“;”联接似乎很幼稚(再次,很多边缘情况)。
P.S .:很抱歉,这个标题很难给我一个有意义的标题:/
答案 0 :(得分:0)
最终,我找到了一种解决方法:通过用自己的函数覆盖eval
命令,我能够更改打印堆栈跟踪的方式,以解决eval
语句中发生的错误。
eval() {
# pre eval logic
command eval "$@"
# post eval logic
}
无论如何,请不要使用eval
,否则请仅使用一个行参数:
# GOOD: "easy" to deal with
for i in ...; do
eval "$(some command)"
done
# BAD: this will mess up your line numbers
eval "$(for i in ...; do
some command $i
done)"