Bash - シェル変数の関数

Bash - シェル変数の関数

私はこれを読んでいます郵便はがきBash シェル変数で関数を使用する場合。シェル関数を使用するには、次のようにその関数をエクスポートし、サブシェルで実行する必要があることがわかりました。

$ export foo='() { echo "Inside function"; }'
$ bash -c 'foo'
Inside function

エクスポートを使用したり、新しいシェルを実行しなくても、現在のシェルで実行できるように bash シェル変数で関数を定義できるかどうかを尋ねたいと思います。

答え1

いいえ、できません。

現在のシェルで関数を使用するには、関数を定義して使用します。

$ foo() { echo "Inside function"; }
$ foo
Inside function

今後シェルショックこれで、変数に関数を保存し、変数をエクスポートし、サブシェルで関数を使用できるようになりました。エクスポートされたbash関数がサポートされているためbash関数定義を環境変数に入れます。良い:

foo=() {
  echo "Inside function"
}

=その後、関数はスペースで置き換えて解釈されます。関数を変数に入れ、変数を参照して関数を実行する意図はありません。

今、その後スティーブン・チャジェラスエラーが見つかりました。この機能は削除されました。、関数定義は通常の環境変数に保存されません。エクスポートされた関数は、環境変数との競合を避けるためにBASH_FUNC_プレフィックスとサフィックスを追加してエンコードされます。これで、インタプリタは変数の内容に関係なくシェル関数かどうかを判断できます。また、サブシェルで使用するには、関数を定義して明示的にエクスポートする必要があります。%%bash

$ foo() { echo "Inside function"; }
$ export -f foo
$ bash -c foo
Inside function

とにかく、あなたの例が現在のバージョンで動作している場合は、bash脆弱なバージョンを使用しています。

答え2

foo='foo(){ echo "Inside Function"; }'
bash -c "$foo; foo"

Inside Function

結局のところ、関数は名前付きで事前に解析された静的コマンド文字列であり、シェルメモリに格納されます。したがって、唯一の実際の方法は、文字列をコードとして評価することです。これは、シェルが関数を呼び出すたびに実行するのとまったく同じです。

以前も変わらず、今もそうです。開発者はこのために便利な方法を作成しており、発生する可能性のあるセキュリティホールを最大限に防止しようとしますが、実際には便利なほど知っておく必要があるよりも少ないことがわかります。

持つたくさん親シェルから子シェルにデータをインポートするときに使用できるオプション。これに慣れて、潜在的な用途と潜在的な弱点を認識するのに十分な自信を持って、慎重で効果的に使用してください。


ディスカッション中に1つまたは2つのことを詳しく説明できるようです。おそらく、あるシェルから別のシェルに静的コマンドを渡すための最良の方法でしょう。(サブシェルコマンドチェーンの上流または下流)はいaliasaliasこの場合に便利です。POSIXの指定これを定義するコマンドを報告します。安全に:

エイリアスを表示するための形式(オペランドが指定されていない場合、または名前オペランドのみが指定されている場合)は、次のようになります。

  • "%s=%s\n", name, value

これ文字列は、シェルに再入力するのに適した適切な引用符で記述する必要があります。シェル引用の説明を見る引用する

たとえば、

alias foo="foo(){ echo 'Inside Function'; }"
alias foo

foo='foo(){ echo '\''Inside Function'\''; }'

それが引用符にどのような影響を与えるのか見てみましょうか?正確な出力はシェルによって異なりますが、通常はシェルを使用して再入力できるalias安全な方法で定義を報告できます。eval同じシェル。ただし、場合によっては、ソース/ターゲットシェルを混合して一致させると問題が発生する可能性があります。

これらの慎重なアプローチを正当化するには:

ksh -c 'alias foo="
    foo(){
        echo \"Inside Function\"
    }"
    alias foo'

foo=$'\nfoo(){\n\techo "Inside Function"\n}'

さらにalias、ネームスペースは3つの別々のネームスペースのうちの1つを表し、通常、ほとんどすべての最新のシェルで完全にマテリアライズされたネームスペースを見つけることができると期待できます。通常、シェルは次の3つの名前空間を提供します。

  • 可変的な:name=value外のリスト

    • 場合によっては、組み込み関数(たとえば)を使用して定義することもできますexportsetreadonly
  • 機能:name() compound_command <>any_redirects存在するコマンド位置

    • シェルが指定されましたいいえ定義されたコマンドで見つかる可能性のある関数定義の一部を拡張または解釈します。
      • ただし、この禁止にはエイリアスは含まれません。エイリアスはシェルコマンドの読み取りの解析中に拡張されるため、alias値が正しいコンテキストで見つかると、検出alias時に関数定義コマンドに拡張されます。
    • 前述のように、シェルは定義コマンドの解析された値をメモリ内の静的文字列として保持し、後で呼び出し後に解析後に関数名が見つかった場合にのみ、文字列で見つかったすべての拡張と拡張を実行します。 。
  • ニックネーム:alias name=value存在するコマンド位置

    • 関数と同様に、alias定義は後でコマンドの場所でその名前を見つけると解釈されます。
    • ただし、関数とは異なり、コマンドの引数として定義されるため、そのalias関数内で見つかった有効なシェル拡張も定義時に解釈されます。したがって、alias定義は常に2回解釈されます。
    • 関数とは異なり、alias定義は定義されたときにまったく構文解析されませんが、シェルのパーサーはその値が見つかるとその値をコマンド文字列として準備します。

alias上記の関数anまたはaの間の主な違い、つまり各関数のシェル拡張ポイントを考慮した内容に気づくでしょう。実際、これらの違いは、これらの組み合わせを非常に便利にすることができます。

alias foo='
    foo(){
        printf "Inside Function: %s\n" "$@"
        unset -f foo
    };  foo "$@" '

これは呼び出し時に自分自身を設定解除する関数を定義するエイリアスなので、別の名前空間を適用してある名前空間に影響します。次に関数を呼び出します。この例は一般的な状況ではあまり役に立ちませんが、次の例では他の状況で提供できる有用性の限られた例を示します。

sh -c "
    alias $(alias foo)
    foo \'
" -- \; \' \" \\

Inside Function: ;
Inside Function: '
Inside Function: "
Inside Function: \
Inside Function: '

alias定義された名前を見つけるのではなく、常に入力行から名前を呼び出す必要があります。これは、コマンドが解析される順序に関連しています。一緒に使用すると、エイリアスと関数を一緒に使用して、情報とパラメータの配列をサブシェルのコンテキストの内外に移動でき、安全で効率的に移動できます。


関連性はほとんどありませんが、ここに別の興味深いものがあります。

alias mlinesh='"${SHELL:-sh}" <<"" '

対話型プロンプトで実行すると、呼び出された行と同じ実行行から渡されたすべての引数がサブシェルの引数として解釈されます。ただし、空の行が最初に表示されるまでのすべての後続の行は、現在のシェルから読み取られ、呼び出されているサブシェルに標準入力として渡され、いかなる方法でも解釈されません。

$ mlinesh -c '. /dev/fd/0; foo'
> foo(){ echo 'Inside Function'; } 
> 

Inside Function

...ただ...

$ mlinesh
> foo(){ echo 'Inside Function'; }
> foo
> 

...もっと簡単に...

関連情報