bash の呼び出し元認識関数

bash の呼び出し元認識関数

呼び出し元でいくつかの変数を効果的に宣言して割り当てる関数 (= 呼び出し先) があります。また、呼び出し元の名前もわかるようにする必要があります。

eval今のところ、渡された変数に呼び出し元によって編集される文字列を書き込むことで前者を実現しています。

 write_local_var_assignments variable_name; eval "$variable_name"

後者は、呼び出し元に渡すか"$FUNCNAME"、呼び出し先に組み込みを呼び出しcallerてその出力を解析させることで実現できると思います。

これらの解決策はすべて非常に不格好に思えるので、2 つの質問があります。

  1. 呼び出し側の協力なしに、呼び出し先が呼び出し元のコンテキストにローカル変数を割り当てることは可能ですか?

つまり、圧縮できますか:

 write_local_var_assignments variable_name; eval "$variable_name"

ちょうど

 run_local_var_assignments

?

  1. 関数の呼び出し元の名前を取得するより良い方法はありますか? 解析やコマンドの置換を行わずに結果を直接取得できれば便利です。

答え1

bash(および、、、、、ksh88)では、ローカル変数のスコープは動的です。mkshyashdashzsh

このコード:

f() { a=2; echo "f: $a"; }
g() { local a=1; f; echo "g: $a"; }
a=0
g
echo "global: $a"

次の出力が生成されます:

f: 2
g: 2
global: 0

これは から呼び出されるため、の変数fが更新されます。g$ag

これは、関数内で ( )typeset構文で宣言された変数や、内で宣言された変数の場合とは対照的です。この場合、次のようになります。kshfunction f { ...; }ksh93privatezsh

f: 2
g: 1
global: 2

その場合は何もする必要はありません。

呼び出し元の関数名を知るには、次のようにbashします。${FUNCNAME[1]}

同等のものは以下のzshとおりです$funcstack[2]:

$ zsh -c 'f() { echo $funcstack[2]; }; g() { f; }; g'
g
$ bash -c 'f() { echo "${FUNCNAME[1]}"; }; g() { f; }; g'
g

答え2

コマンド置換を使用しても構わない場合は、呼び出し元に自己評価された文字列を返すことができる関数があり、run_local_var_assignments次のようにして作成した関数を呼び出すことができます。

$(run_local_var_assignments)

関数は次のようになります。

emit () {
  local IFS=$'\n'
  printf 'eval eval %q' "$*"
}

これは、上記とよく似た eval ステートメントを生成しますeval "$variable_name"。これは結果の行の最初にあるため、bash は生成された eval をコマンドとして実行します。

関数によって実行される二重の評価と %q エスケープは、改行やその他の特殊文字を呼び出し元に正しく渡すために必要なためだけに存在します。

関数は次のように記述できます。

run_local_var_assignments () {
  local assignments=()
  assignments+=( 'local myvar1="my value1"' )
  assignments+=( 'local myvar2="my value2"' )
  emit "${assignments[@]}"
}

割り当てはソース コードと同じように記述されるため、値内のスペースなどは上記のように引用符で囲む必要があります。

コマンド置換でこの関数を呼び出すと (この回答の先頭にあるように)、呼び出し元のスコープ内にそれらの値を持つローカル変数が作成されます。

ステートメントの配列の代わりに通常の文字列またはヒアドキュメントを使用することもできますが、ステートメントのブロックを動的に構築するには配列メソッドが便利であることがよくあるので、少し複雑ではありますが、それを示すことにしました。

関連情報