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 0; }ここでは1つのコマンドしか実行されないため、return()の周りの中括弧はオプションです。しかし、彼らは必須です他のすべてのケースでは、一貫性のために通常置きます。それらが存在しない場合、演算子の優先順位のため、このような操作は[[ ... ]] && 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
}

(初期化関数が別のモジュールにある場合は、dotコマンドを使用してそのモジュールを取得します。)

関連情報