我刚刚创建了与BusyBox类似的库脚本。在其中,我使用以下函数来测试它是否是来源...
function isSourced () {
[[ "${FUNCNAME[1]}" == "source" ]] && return 0
return 1
}
Bash维护的FUNCNAME数组本质上是一个函数调用堆栈。$FUNCNAME
(或${FUNCNAME[0]}
)是当前正在执行的函数的名称。${FUNCNAME[1]}
是调用它的函数的名称,依此类推。
最顶层的项是脚本本身的特殊值。它将包含...
- 单词“ source”(如果源脚本)
- 如果正在执行脚本且正在通过函数进行测试,则为“ main”一词
- “”(空),如果脚本正在执行并且测试在任何功能之外进行,即……在脚本本身的级别。
上面的函数实际上仅在脚本级别调用时才起作用(这是我所需要的)。如果从另一个函数内部调用它将失败,因为数组项号将是错误的。为了使它在任何地方都可以工作,需要找到栈顶并测试该值,这更加复杂。
如果需要,可以使用以下命令获取“堆栈顶部”的阵列项目编号:
local _top_of_stack=$(( ${#FUNCNAME[@]} - 1 ))
${#FUNCNAME[@]}
是数组中的项目数。作为从零开始的数组,我们减去1得到最后一个项目。
这三个函数一起使用以产生类似于Python的函数堆栈跟踪,它们可能使您更好地了解所有这些工作原理...
function inspFnStack () {
local T+=" "
local _at=
local _text="\n"
local _top=$(inspFnStackTop)
local _fn=${FUNCNAME[1]}; [[ $_fn =~ source|main ]] || _fn+="()"
local i=_top; ((--i))
#
_text+="$i item function call stack for $_fn ...\n"
_text+="| L BASH_SOURCE{BASH_LINENO called from}.FUNCNAME \n"
_text+="| ---------------------------------------------------\n"
while (( $i > 0 ))
do
_text+="| $i ${T}$(inspFnStackItem $i)\n"
T+=" "
((--i))
done
#
printf "$_text\n"
#
return 0
}
function inspFnStackItem () {
local _i=$1
local _fn=${FUNCNAME[$_i]}; [[ $_fn =~ source|main ]] || _fn+="()"
local _at="${BASH_LINENO[$_i-1]}"; [[ $_at == 1 ]] && _at="trap"
local _item="${BASH_SOURCE[$_i]}{${_at}}.$_fn"
#
printf "%s" "$_item"
return 0
}
function inspFnStackTop () {
# top stack item is 1 less than length of FUNCNAME array stack
printf "%d\n" $(( ${#FUNCNAME[@]} - 1 ))
#
return 0
}
请注意,FUNCNAME,BASH_SOURCE和BASH_LINENO是bash维护的3个数组,就好像它们是一个三维数组一样。