bash:エクスポートされた関数は表示されませんが、変数は表示されます。

bash:エクスポートされた関数は表示されませんが、変数は表示されます。

長年にわたり、私はシェルとスクリプトによって参照されているbash関数ライブラリを収集しました。定型句のインポートを減らすために、スクリプトにライブラリを賢く含める方法のオプションを探しています。

私の解決策は2つの部分で構成されています。まず、構成(環境変数)をインポートしてから、関数ライブラリをインポートします。

~/bash_envs:(構成)

export SOME_VAR=VALUE
export SHELL_LIB=/path/to/library.sh

# convenience funtion, so scripts who source env_vars file (ie this file) can
# simply call it, instead of including the same block in each file themselves.
function _load_common() {
    # import common library/functions:
    source $SHELL_LIB
}
export -f _load_common

# marker var used to detect whether env vars (ie this file) have been loaded:
export __ENV_VARS_LOADED_MARKER_VAR=loaded

これで、スクリプトで次のコードを実行します。

if [[ "$__ENV_VARS_LOADED_MARKER_VAR" != loaded ]]; then  # in our case $__ENV_VARS_LOADED_MARKER_VAR=loaded, ie this block is not executed
    USER_ENVS=/home/laur/bash_envs

    if [[ -r "$USER_ENVS" ]]; then
        source "$USER_ENVS"
    else
        echo -e "\n    ERROR: env vars file [$USER_ENVS] not found! Abort."
        exit 1
    fi
fi

_load_common

_load_common: command not foundこれにより例外が生成されます。なぜそんなことですか?コメントは__ENV_VARS_LOADED_MARKER_VAR=loadedうまくエクスポートされて表示されるため、sourceする理由はありませんが、__ENV_VARS_LOADED_MARKER_VARと同じ場所からエクスポートされたにもかかわらず関数を見つけることができません$USER_ENVS_load_common()

答え1

質問

観察する:

$ bash -c 'foobar () { :; }; export -f foobar; dash -c env' |grep foobar
$ bash -c 'foobar () { :; }; export -f foobar; bash -c env' |grep foobar
BASH_FUNC_foobar%%=() {  :
$ bash -c 'foobar () { :; }; export -f foobar; ksh93 -c env' |grep foobar
BASH_FUNC_foobar%%=() {  :
$ bash -c 'foobar () { :; }; export -f foobar; mksh -c env' |grep foobar
$ bash -c 'foobar () { :; }; export -f foobar; zsh -c env' |grep foobar
BASH_FUNC_foobar%%=() {  :
$ bash -c 'foobar () { :; }; export -f foobar; busybox sh -c env' |grep foobar
BASH_FUNC_foobar%%=() {  :

環境変数はUnixオペレーティングシステムの機能です。これに対するサポートはカーネルまで拡張されます。つまり、あるプログラムが別のプログラムを呼び出すとき(次を使用して)execveシステムコール)、呼び出しパラメータの1つは新しいプログラムの環境です。

shスタイルシェル(dash、bash、kshなど)に組み込まれているコマンドを使用すると、exportシェル変数が環境変数として使用され、シェルが呼び出すプロセスに送信されます。代わりに、シェルが呼び出されると、すべての環境変数はそのシェルインスタンスのシェル変数になります。

関数のエクスポートはbashの機能です。 Bashは、名前が関数名から派生し、値が関数本体(ヘッダーとトレーラーを含む)である環境変数を生成して、関数を「エクスポートします」。上記では、環境変数の名前がどのように構成されているかを見ることができます。BASH_FUNC_それには関数の名前があります%%

この名前はシェル変数の有効な名前ではありません。シェルは起動時に環境変数をシェル変数として取得します。環境変数の名前が有効なシェル変数でない場合、シェルごとに動作が異なります。一部は変数を子プロセス(上記:bash、ksh93、zsh、BusyBox)に渡し、他のいくつかはエクスポートされたシェル変数を子プロセス(上記:dash、mksh)に渡し、a以外の名前を持つ環境変数効果的に削除されました。有効なシェル変数(空でないASCII文字、数字、およびシーケンス_)。

もともとbashは、関数と同じ名前の環境変数を使用して、デフォルトでこの問題を回避しました。 (ほとんどの場合のみ:関数名にはシェル変数名に許可されていない文字を含めることができます(例:))-。としてエクスポートされた名前が正しい)。 )は環境の他の)を上書きします。決定的にbashが変更されました。もともとの実装により、深刻なセキュリティの脆弱性が発見されました。。 (あなたも見ることができますenv x='() { ::};どういう意味ですか? bashコマンドは何をし、なぜ安全ではないのですか?Shellshock(CVE-2014-6271/7169)バグはいつ紹介されましたか?このバグを完全に修正したパッチは何ですか?Shellshock Bashの脆弱性はどのように発見されましたか?)この変更の1つの欠点は、エクスポートされた機能がダッシュやmkshを含むいくつかのプログラムを通過できなくなることです。

システムにダッシュがある可能性があります/bin/sh。これは非常に人気のある選択です。よく使用されるため、呼び出しパスのどこかで/bin/sh関数を使用しようとしているbashインスタンスとして実行された元のbashインスタンスが呼び出された可能性があります。有効な変数名があるため、渡しますが通過しません。shexport -f _load_common__ENV_VARS_LOADED_MARKER_VARBASH_FUNC__load_common%%

解決策

エクスポートした関数を使用しないでください。まず、彼らは役に立たない。彼らはあなたに全く役に立たない。関数のエクスポートの唯一の利点は、bashを呼び出すときにスクリプトで関数を定義し、それを呼び出すbashインスタンスに渡すなど、どこかで関数定義を読み取るためfind -execxargsbashインスタンスが必要ないことですparallel。しかし、あなたの場合、関数定義を読み取るコードはすでにあります。だから無条件の関数定義を読んでください。削除しexport -f _load_commonて削除して__ENV_VARS_LOADED_MARKER_VARから呼び出しますsource "$USER_ENVS"

関連情報