以下の方法がいくつかの場面でどのように機能するかについて、その方法と理由についてはすでに説明したので、ここでは再度説明しません。個人的には、このトピックに関する私自身のお気に入りはこことここです。
あなたがそれを読むことに興味はないが、それでも興味がある場合は、関数の入力に添付されたヒアドキュメントが関数の実行前にシェル展開について評価され、関数が定義されたときの状態で新しく生成されることを理解してください関数が呼び出されるたび。
宣言する
他の関数を宣言する関数が必要です。
_fn_init() { . /dev/fd/4 ; } 4<<INIT
${1}() { $(shift ; printf %s\\n "$@")
} 4<<-REQ 5<<-\\RESET
: \${_if_unset?shell will ERR and print this to stderr}
: \${common_param="REQ/RESET added to all funcs"}
REQ
_fn_init $(printf "'%s' " "$@")
RESET
INIT
それを実行します
ここでは_fn_init
、という関数を宣言するように要求しますfn
。
set -vx
_fn_init fn \
'echo "this would be command 1"' \
'echo "$common_param"'
#OUTPUT#
+ _fn_init fn 'echo "this would be command 1"' 'echo "$common_param"'
shift ; printf %s\\n "$@"
++ shift
++ printf '%s\n' 'echo "this would be command 1"' 'echo "$common_param"'
printf "'%s' " "$@"
++ printf ''\''%s'\'' ' fn 'echo "this would be command 1"' 'echo "$common_param"'
#ALL OF THE ABOVE OCCURS BEFORE _fn_init RUNS#
#FIRST AND ONLY COMMAND ACTUALLY IN FUNCTION BODY BELOW#
+ . /dev/fd/4
#fn AFTER _fn_init .dot SOURCES IT#
fn() { echo "this would be command 1"
echo "$common_param"
} 4<<-REQ 5<<-\RESET
: ${_if_unset?shell will ERR and print this to stderr}
: ${common_param="REQ/RESET added to all funcs"}
REQ
_fn_init 'fn' \
'echo "this would be command 1"' \
'echo "$common_param"'
RESET
必須
この関数を呼び出したい場合、環境変数_if_unset
が設定されていないと機能しません。
fn
#OUTPUT#
+ fn
/dev/fd/4: line 1: _if_unset: shell will ERR and print this to stderr
シェルトレースの順序に注意してください- が設定さfn
れていないときに呼び出されたときに失敗するだけでなく、最初から実行されることもありません。これはヒアドキュメントの展開を扱うときに理解する最も重要な要素です- 結局のところ、展開は常に最初に行わなければなりません。_if_unset
<<input
エラーは/dev/fd/4
、親シェルが入力を関数に渡す前に評価しているために発生します。これは、必要な環境をテストする最も簡単で効率的な方法です。
とにかく、失敗は簡単に解決されます。
_if_unset=set fn
#OUTPUT#
+ _if_unset=set
+ fn
+ echo 'this would be command 1'
this would be command 1
+ echo 'REQ/RESET added to all funcs'
REQ/RESET added to all funcs
フレキシブル
変数common_param
は、によって宣言されたすべての関数の入力時にデフォルト値に評価されます_fn_init
。しかし、その値は他のどの値にも変更可能であり、同様に宣言されたすべての関数によって尊重されます。ここではシェルトレースを省略します。ここでは、未知の領域には行きません。
set +vx
_fn_init 'fn' \
'echo "Hi! I am the first function."' \
'echo "$common_param"'
_fn_init 'fn2' \
'echo "This is another function."' \
'echo "$common_param"'
_if_unset=set ;
上記では、2つの関数を宣言して設定してい_if_unset
ます。ここで、どちらかの関数を呼び出す前に、設定を解除common_param
して、呼び出したときに自分で設定することを確認します。
unset common_param ; echo
fn ; echo
fn2 ; echo
#OUTPUT#
Hi! I am the first function.
REQ/RESET added to all funcs
This is another function.
REQ/RESET added to all funcs
そして今、呼び出し元のスコープから:
echo $common_param
#OUTPUT#
REQ/RESET added to all funcs
しかし今、私はそれを完全に別のものにしたいです:
common_param="Our common parameter is now something else entirely."
fn ; echo
fn2 ; echo
#OUTPUT#
Hi! I am the first function.
Our common parameter is now something else entirely.
This is another function.
Our common parameter is now something else entirely.
そして、私が設定解除した場合_if_unset
?
unset _if_unset ; echo
echo "fn:"
fn ; echo
echo "fn2:"
fn2 ; echo
#OUTPUT#
fn:
dash: 1: _if_unset: shell will ERR and print this to stderr
fn2:
dash: 1: _if_unset: shell will ERR and print this to stderr
リセット
関数の状態をいつでもリセットする必要がある場合は、簡単に実行できます。(関数内から)行うだけです:
. /dev/fd/5
最初に関数を宣言するために使用した引数を5<<\RESET
入力ファイル記述子に保存しました。その.dot
ため、いつでもシェルでそれを調達すると、最初に設定したプロセスが繰り返されます。POSIXが実際にはファイル記述子デバイスのノードパスを指定していない(シェルのに必要です.dot
)という事実を見落としても構わないのであれば、これらはすべて非常に簡単で、本当に完全に移植可能です。
この動作を簡単に拡張して、関数のさまざまな状態を構成できます。
もっと?
ちなみに、これは表面をかすかに傷つけるだけです。私はよくこれらのテクニックを使用して、いつでも宣言できる小さなヘルパー関数をメイン関数の入力に埋め込みます。たとえば、$@
必要に応じて追加の位置配列を使用します。実際、私が信じているように、高次シェルがとにかくこれに非常に近いものでなければなりません。プログラムで名前を付けるのは非常に簡単です。
Iまたなどのパラメータの限定された種類を受け入れ、次いでラムダの線に沿って一回使用または他のスコープ限定バーナー機能を定義ジェネレータ関数宣言する-単にこと-又はインライン関数をunset -f
「とき自体よ使って。シェル関数を渡すことができます。