同じ名前のシェル関数と変数

同じ名前のシェル関数と変数

~からバッシュマニュアル:

同じ名前を持つシェル関数と変数は、シェルサブアイテムに渡される同じ名前を持つ環境に複数のアイテムを作成できます。これにより問題が発生する可能性がありますのでご注意ください。

bashは「シェル関数と同じ名前を持つ変数」をどのように区別しますか?

$  func () { return 3; }; func=4; declare -p func; declare -f func;
declare -- func="4"
func () 
{ 
  return 3
}

「環境で同じ名前を持つ複数の項目がシェルの子として渡される」場合はいつですか?

問題が発生したときに何に「注意」する必要がありますか?

答え1

全体的な状況:独立した名前空間

通常、シェルは変数と関数が異なるコンテキストで使用されるため、これを区別します。簡単に言えば、名前はaの後に表示される$か、組み込み関数の引数として表示される場合(たとえばexport(without -f)やunset(without))、変数名です。-f名前は、コマンド(別名拡張後)または引数などで表示される場合の関数export -f名です。unset -f

変数を環境にエクスポートできます。環境変数は、シェル変数と同じ名前(および同じ値)を持ちます。

以前のbashの場合:関数のエクスポートによる混乱

他のほとんどのシェルとは異なり、Bashは機能を環境にエクスポートすることもできます。環境にはタイプマークがないため、環境内の項目が環境変数の名前または値を分析する以外に機能であるかどうかを識別する方法はありません。

以前のバージョンの bash では、関数名を名前として使用し、関数定義に似たものを関数値として使用して、環境に関数を保存しました。たとえば、

bash-4.1$ foobar () { echo foobar; }
bash-4.1$ export -f foobar
bash-4.1$ env |grep -A1 foobar
foobar=() {  echo foobar
}
bash-4.1$ 

{ echo foobar; }コードは、次の関数と値が次の変数() { echo foobar␤}(ここでは改行文字です)との間に違いはありません。これは誤った設計決定であることが判明した。

場合によっては、潜在的に敵対的なオブジェクトによって値が制御される環境変数を使用してシェルスクリプトが呼び出されます。たとえば、CGIスクリプトです。 Bashの関数のエクスポート/インポート機能を使用すると、この方法で関数を注入できます。たとえば、スクリプトを実行します。

#!/bin/bash
ls

環境に特定の名前(たとえば)を持つ変数が含まれていない限り、PATHリモート要求からの要求は安全です。しかし、リクエストが環境変数lsをに設定できる場合、それは関数の本文であるため、() { cat /etc/passwd; }bashはこれを喜んで実行します。cat /etc/passwdls

最新のbashの使用:はるかに混乱していません。

このセキュリティの脆弱性は、次のために発生します。スティーブン・チャジェラス片側にシェルショックバグ。 Shellshock以降のバージョンのbashからエクスポートされた関数は、次のように識別されます。名前内容よりも。

bash-4.3$ foobar () { echo foobar; }
bash-4.3$ export -f foobar
bash-4.3$ env |grep -A1 foobar
BASH_FUNC_foobar%%=() {  echo foobar
}

BASH_FUNC_foobar%%同様の名前は通常コマンド名として使用されず、環境変数を渡すことができるインターフェイスを介してフィルタリングできるため、セキュリティ上の問題はありません。技術的には、%環境変数名に文字を含めることができます(これが現代bashのエクスポートされた関数が機能する理由です)。しかし、通常、シェルは%変数名を受け入れないため、人々はそうしません。

bashマニュアルのこの文は、以前(Shellshock以前)の動作を示しています。更新または削除する必要があります。最新のbashバージョンの場合は、環境変数名が%%

答え2

Emacs Lispでも同様の状況が発生します。これには2つの名前空間があります。1つは関数用、もう1つは変数用です。(var)関数コンテキスト()で記号を逆参照すると関数が呼び出され、変数コンテキスト(var括弧なし)で記号を逆参照すると変数が提供されます。たとえば、

(defun myvar (myvar)
  "adds 3 to MYVAR"
  (+ 3 myvar))
(setq myvar 7)
(message (myvar myvar))

実行されます機能 myvarパラメータは7変数の逆参照ですmyvar

慣れていないととても混乱することがあります。

問題を確認してテストした後強く打つ同じ振る舞いを見せることに驚きました。上記のELispをbashに変換します。

[grochmal@phoenix ~]$ myvar () { echo $(($1+3)); }
[grochmal@phoenix ~]$ myvar=7
[grochmal@phoenix ~]$ myvar $myvar
10

$この点で、Bashは変数にラベルを付ける必要があるため、ELispよりも混乱しています。それでも、この名前はdeclare2つをカバーする名前のように見えるかもしれません。望むより:

[grochmal@phoenix ~]$ declare -p myvar
declare -x myvar="7"
[grochmal@phoenix ~]$ declare -f myvar
myvar () 
{ 
    echo $(($1+3))
}

(PS 2つの名前空間の存在に慣れたら、例えば、しばらくの間ELISpでプログラミングをしたら、これ以上混乱しません。)

関連情報