bash shell 能不能打印出类似Java的异常栈(Stack Trace)呢?
Java的异常栈会打印出调用方法链的具体位置,具体信息是<文件,函数,行号>三元组。类似下面:
StudentException: Error finding students
at StudentManager.findStudents(StudentManager.java:13)
at StudentProgram.main(StudentProgram.java:9)
BASH_SOURCE是一个数组变量,存储元素文件名。
FUNCNAME也是一个数组变量,存储元素是函数名。
这两个数组是按索引对应的,${BASH_SOURCE[0]}代表的是异常栈底部当前文件名,${FUNCNAME[0]}代表的异常栈底部的当前函数。
BASH_SOURCE
An array variable whose members are the source filenames where the corresponding shell function names in theFUNCNAMEarray variable are defined. The shell function${FUNCNAME[$i]}is defined in the file${BASH_SOURCE[$i]}and called from${BASH_SOURCE[$i+1]}
FUNCNAME
An array variable containing the names of all shell functions currently in the execution call stack. The element with index 0 is the name of any currently-executing shell function. The bottom-most element (the one with the highest index) is “main”.
写个demo验证一下
source test2.sh
function fun_b() {
echo "Info in ${BASH_SOURCE[0]}:${FUNCNAME[0]}:${BASH_LINENO[0]} "
fun_c
}
function fun_a() {
echo "Info in ${BASH_SOURCE[0]}:${FUNCNAME[0]}:${BASH_LINENO[0]} "
fun_b
}
fun_a
function fun_c() {
echo "Info in ${BASH_SOURCE[0]}:${FUNCNAME[0]}:${BASH_LINENO[0]} "
}
其中
${BASH_LINENO[0]}代表函数调用语句的行号
执行test.sh查看异常栈内容:
Info in test.sh:fun_a:14
Info in test.sh:fun_b:11
Info in test2.sh:fun_c:5
把上面那个demo稍微润一下,可以实现一个很好的生产级函数:
function errexit() {
local err=$?
set +o xtrace
local code="${1:-1}"
echo "Error in ${BASH_SOURCE[1]}:${BASH_LINENO[0]}. '${BASH_COMMAND}' exited with status $err"
# Print out the stack trace described by $function_stack
if [ ${#FUNCNAME[@]} -gt 2 ]
then
echo "Call tree:"
for ((i=1;i<${#FUNCNAME[@]}-1;i++))
do
echo " $i: ${BASH_SOURCE[$i+1]}:${BASH_LINENO[$i]} ${FUNCNAME[$i]}(...)"
done
fi
echo "Exiting with status ${code}"
exit "${code}"
}
function errexit() {
local err=$?
set +o xtrace
local code="${1:-1}"
echo "Error in ${BASH_SOURCE[1]}:${BASH_LINENO[0]}. '${BASH_COMMAND}' exited with status $err"
# Print out the stack trace described by $function_stack
if [ ${#FUNCNAME[@]} -gt 2 ]
then
echo "Call tree:"
for ((i=1;i<${#FUNCNAME[@]}-1;i++))
do
echo " $i: ${BASH_SOURCE[$i+1]}:${BASH_LINENO[$i]} ${FUNCNAME[$i]}(...)"
done
fi
echo "Exiting with status ${code}"
exit "${code}"
}
function func_c() {
cat hello # some danger
res=$?
if [ $res -ne 0 ]
then
errexit $res
fi
}
# trap ERR to provide an error handler whenever a command exits nonzero
# this is a more verbose version of set -o errexit
# If a sigspec is ERR, the command arg is executed whenever a simple command has a non-zero exit status
trap 'errexit' ERR
# setting errtrace allows our ERR trap handler to be propagated to functions,
# expansions and subshells
set -o errtrace
function func_c() {
cat hello # bad command exit with code 1
}
function func_a() {
func_c
}
source test2.sh
function func_b() {
func_a
}
func_b
测试一下:
调用链是 func_b ->func_a -> func_c ,打印出的内容函数前的文件名是调用语句所在的文件名,行号是调用语句所在的行号
[root@localhost utiltest]# bash test.sh
cat: hello: No such file or directory
Error in test2.sh:21. 'cat hello' exited with status 1
Call tree:
1: test2.sh:33 func_c(...)
2: test.sh:5 func_a(...)
3: test.sh:7 func_b(...)
Exiting with status 1
上面有几个符号还是注释一下
# set +o 代表关闭选项,set -o 代表启动选项
# set -x (long notation: set -o xtrace) traces commands before executing them.
set +o xtrace
这个是关闭debug信息,要不然会打印出类似这种(这是bash shell的运行时值计算):
Call tree:
+ (( i=1 ))
+ (( i<5-1 ))
+ echo ' 1: test2.sh:28 func_c(...)'
1: test2.sh:28 func_c(...)
+ (( i++ ))
+ (( i<5-1 ))
${#FUNCNAME[@]}代表FUNCNAME数组长度。
# code赋值为函数第一个参数(占位符),否则赋值为1.(前缀用了'-'字符,这里不是-1)
local code="${1:-1}"
trap机制是当发生异常时,也就是命令以非0状态退出时,需要调用异常处理程序。
trap defines and activates handlers to run when the shell receives signals or other special conditions.
# 当有命令非零退出时,执行errexit句柄(函数)
trap 'errexit' ERR
执行errexit时,要打印出哪个命令是异常退出了,使用了BASH_COMMAND变量。这个变量在trap条件下读取时,值为异常退出语句,也就是我们想要的。
BASH_COMMAND
The command currently being executed or about to be executed, unless the shell is executing a command as the result of a trap, in which case it is the command executing at the time of the trap
另外为了能够将这个触发机制传播到子shell(bash每次执行函数也是执行一个子shell):
-E, equal to set -o errtrace
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.
否则trap机制要在每个可能引起错误的shell函数中定义一次,才能实现相同的效果。比如:
function func_c() {
trap 'errexit' ERR
cat hello # some danger
}