以下函数作为每个其他函数的第一行被调用,以便处理可选的调试、上下文相关帮助等。因此,调用一个函数进而调用另一个函数可能(通常会)导致循环引用。
如何在不损失功能的情况下避免循环引用?
function fnInit () {
###
### on return from fnInit...
### 0 implies "safe to continue"
### 1 implies "do NOT continue"
###
#
local _fn=
local _msg=
#
### handle optional debugging, context sensitive help, etc.
#
[[ "$INSPECT" ]] && { TIMELAPSE= ...; }
### fnInit --inspect
#
[[ "$1" == --help ]] && { ... ; return 1; }
### fnInit --help
# :
# :
return 0
}
答案1
如果函数 fnInit()(例如当前函数 - ${FUNCNAME[0]})也出现在函数调用堆栈的较高位置,则进行救助。
在下面的结果版本中...
- $_fna 是一个字符串,其中包含函数调用堆栈中以空格分隔的函数名称列表,以调用该函数的函数开始(第 1 级及以上)。
- 该习惯用法
"${_fna/${FUNCNAME[0]}/}"
从 $_fna 中减去当前函数名称。 [[ "${_fna/${FUNCNAME[0]}/}" != "${_fna}" ]]
如果减法结果与原始结果相同(因此没有减去任何内容),则比较返回 true(0)- 列表处理控制运算符
&&
表示当且仅当前面的命令([[ ... ]]
)的返回码为 true(0) 时才执行后面的命令。 此处return (
{ return 0; }
) 周围的大括号是可选的,因为只有一个命令要执行。然而它们是强制性的在所有其他情况下,所以我通常将它们放入以保持一致性。如果没有它们,由于运算符优先级,类似的东西只有在true时[[ ... ]] && dosomething; return 0
才会执行,正如预期的那样。dosomething
[[ ... ]]
但总会执行return 0
。Bash 会将其读作...[[ ... ]] && dosomething return 0
fnInit()
每个函数调用只处理一次...
function fnInit () {
###
### on return from fnInit...
### 0 implies "safe to continue"
### 1 implies "do NOT continue"
###
local _fna="${FUNCNAME[*]:1}"; [[ "${_fna/${FUNCNAME[0]}/}" != "${_fna}" ]] && { return 0; }
### Detect circular reference
### Is function in the function call stack more than once?
###
### Seems like the following should work but it does not...
### if [[ "${FUNCNAME[*]:1/$FUNCNAME/}" != "${FUNCNAME[*]:1}" ]]; then ...
### It appears to fail silently and exit the script so neither 'then'
### nor 'else' branch executes.
### Why?
### per http://unix.stackexchange.com/q/186942/27437...
### In general, you can't use multiple variable expansion modifiers
### in the same expression. – Barmar
###
### Solution:
### Store the array into a temporary string variable.
###
#
local _fn=
local _msg=
#
### handle optional debugging, context sensitive help, etc.
#
[[ "$INSPECT" ]] && { TIMELAPSE= ...; }
### fnInit --inspect
#
[[ "$1" == --help ]] && { ... ; return 1; }
### fnInit --help
# :
# :
return 0
}
答案2
通常这是通过检查和设置状态变量(标志)来完成的。就像是...
function fnInit
{
[[ -z ${this_init_state} ]] || return 0
this_init_state=1
# rest of your code
}
(如果您的初始化函数位于单独的模块中,则使用点命令获取该模块。)