在 bash 4.1.0 中检测循环引用

在 bash 4.1.0 中检测循环引用

以下函数作为每个其他函数的第一行被调用,以便处理可选的调试、上下文相关帮助等。因此,调用一个函数进而调用另一个函数可能(通常会)导致循环引用。

如何在不损失功能的情况下避免循环引用?

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 0Bash 会将其读作...

        [[ ... ]]  && 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
}

(如果您的初始化函数位于单独的模块中,则使用点命令获取该模块。)

相关内容