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. 提到的安全公告讨论了变量后面命令的执行,例如,
x='() { echo I do nothing; }; echo vulnerable' bash -c ':'
                              ^^^^^^^^^^^^^^^
                              This is executed - this vunerability is CLOSED in version 4.3.25(1).

env-definition 之后的命令在最新的 Bash 中没有执行。

但问题仍然存在 -为什么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; }

通常,这样的字符串成为 shell 变量,使用前面的部分=作为名称,后面的部分作为值。然而,Bash 通过定义一个函数来特别对待这些字符串:

$ echo $foo

$ foo
5

您可以通过使用 Bash 以外的其他方式检查环境本身并没有改变它:

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

(显然,在创建子环境时,父 Bash 用换行符替换了分号)。只有子 Bash 从这样的字符串创建函数而不是 shell 变量。

foo在同一个 shell 中既可以是参数也可以是函数的事实;

$ 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环境中代替。脚本中存在的下一个立即调用date给定(未更改)。date调用bash -c "date"创建一个新的 shell 并执行由定义的日期declare -x

于 2014-09-24T20:31:57.943 回答