1

なぜコードは

date
bash -c "date"
declare -x date='() { echo today; }' #aka export date='() { echo today; }'
date
bash -c "date"

印刷する

Wed Sep 24 22:01:50 CEST 2014
Wed Sep 24 22:01:50 CEST 2014
Wed Sep 24 22:01:50 CEST 2014
today

?

評価を行う場所 (および理由)

 date$date

起こり、得る

 date() {echo today; }

広告: @Etan Reisner

  1. 関数ではなく、変数をエクスポートしています。Bashはそれから関数を作成します。の
export date='someting'

その内容に関係なく、変数のままです。では、なぜ

export date='() { echo something; }' #Note, it is a variable, not function.

関数に変換?

  1. 前述のセキュリティ アドバイザリでは、 variable に続くコマンドの実行について説明しています。たとえば、
x='() { echo I do nothing; }; echo vulnerable' bash -c ':'
                              ^^^^^^^^^^^^^^^
                              This is executed - this vunerability is CLOSED in version 4.3.25(1).

最新のBashではenv-definition以降のコマンドが実行されません。

しかし、疑問が残ります。なぜBash は、エクスポートされた変数を関数に変換するのでしょうか?

これはバグです ;) @chepner の回答に基づく完全なデモ:

#Define three variables
foo='() { echo variable foo; }'    # ()crafted
qux='() { echo variable qux; }'    # ()crafted
bar='variable bar'                 # Normal
export foo qux bar                 # Export

#Define the same name functions (but not qux!)
foo() { echo "function foo"; }
bar() { echo "function bar"; }
declare -fx foo bar                 #Export

#printouts
echo "current shell foo variable:=$foo="
echo "current shell foo function:=$(foo)="
echo "current shell bar variable:=$bar="
echo "current shell bar function:=$(bar)="
echo "current shell qux variable:=$qux="
echo "current shell qux function:=$(qux)="

#subshell
bash -c 'echo subshell foo variable:=$foo='
bash -c 'echo subshell foo command :=$(foo)='
bash -c 'echo subshell bar variable:=$bar='
bash -c 'echo subshell bar command :=$(bar)='
bash -c 'echo subshell qux variable:=$qux='
bash -c 'echo subshell qux command :=$(qux)='

版画

current shell foo variable:=() { echo variable foo; }=
current shell foo function:=function foo=
current shell bar variable:=variable bar=
current shell bar function:=function bar=
current shell qux variable:=() { echo variable qux; }=
tt: line 20: qux: command not found
current shell qux function:==
subshell foo variable:==                   #<-- LOST the exported foo variable
subshell foo command :=function foo=
subshell bar variable:=variable bar=
subshell bar command :=function bar=
subshell qux variable:==                   #<-- And the variable qux got converted to
subshell qux command :=variable qux=       #<-- function qux in the subshell (!!!).

長いコメントは避けて、Bash ソースからのコードを次に示します。

 if (privmode == 0 && read_but_dont_execute == 0 && STREQN ("() {", string, 4))
                                                           ^^^^^^^^ THE PROBLEM
    {
      string_length = strlen (string);
      temp_string = (char *)xmalloc (3 + string_length + char_index);

      strcpy (temp_string, name);
      temp_string[char_index] = ' ';
      strcpy (temp_string + char_index + 1, string);

      if (posixly_correct == 0 || legal_identifier (name))
        parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);

      /* Ancient backwards compatibility.  Old versions of bash exported
         functions like name()=() {...} */

「古代」(のようです)の方が良かった... :)

      if (name[char_index - 1] == ')' && name[char_index - 2] == '(')
        name[char_index - 2] = '\0';
4

3 に答える 3

4

覚えておくべき重要なポイントは、

foo='() { echo 5; }'

関数によく似た文字列で文字列パラメーターを定義するだけです。それはまだ通常の文字列です:

$ echo $foo
() { echo 5; }

関数ではありません:

$ foo
bash: foo: command not found

fooエクスポート用にマークされると、

$ export foo

すべての子 Bash は、その環境で次の文字列を認識します。

foo=() { echo 5; }

通常、このような文字列は、 の前の部分を=名前として使用し、値の後の部分を使用して、シェル変数になります。ただし、Bash は代わりに関数を定義することで、そのような文字列を特別に扱います。

$ echo $foo

$ foo
5

Bash 以外で調べると、環境自体が変更されていないことがわかります。

$ perl -e 'print $ENV{foo}\n"'
() { echo 5
}

(親の Bash は、明らかに、子の環境を作成するときにセミコロンを改行に置き換えます)。そのような文字列からシェル変数の代わりに関数を作成するのは、子 Bash だけです。

foo同じシェル内でパラメーターと関数の両方になる可能性があるという事実。

$ foo=5
$ foo () { echo 9; }
$ echo $foo
5
$ foo
9

-fが必要な理由を説明しexportます。export foo文字列foo=5が子の環境に追加されます。export -f foo文字列を追加するために使用されますfoo=() { echo 9; }

于 2014-09-24T20:46:02.303 に答える
0

基本的に、という名前の関数を手動でエクスポートしていますdate。(これは、関数をエクスポートするために bash が内部的に使用する形式であるためです。これは、Barmar の回答で提案されています。このメカニズムについては、少なくともここで言及されています。)

次に、bash を実行すると、エクスポートされた関数が表示され、実行するように指示されたときにそれが使用されますdate

問題は、そのメカニズムがどこに指定されているかです。内部の詳細なので、そうではないと思います。

それが何かに役立つ場合、これは動作のマージを示すはずです。

$ bar() { echo automatic; }; export -f bar
$ declare -x foo='() { echo manual; }'
$ declare -p foo bar
declare -x foo="() { echo manual; }"
-bash: declare: bar: not found
$ type foo bar
-bash: type: foo: not found
bar is a function
bar ()
{
    echo automatic
}
$ bash -c 'type foo bar'
foo is a function
foo ()
{
    echo manual
}
bar is a function
bar ()
{
    echo automatic
}
于 2014-09-24T20:18:19.620 に答える
-1

あなたの質問への答えは から直接得られますman bash:

exportおよびコマンドを使用するdeclare -xと、パラメータと関数を環境に追加したり、環境から削除したりできます。環境内のパラメーターの値が変更された場合、新しい値が環境の一部になり、古い値が置き換えられます。

したがって

declare -x date='() { echo today; }'

環境dateで置き換えます。スクリプトに存在する (変更されていない) ため、次の即時呼び出しでGive が実行されます。を呼び出すと、新しいシェルが作成され、 で定義されているように date が実行されます。datedatebash -c "date"declare -x

于 2014-09-24T20:31:57.943 に答える