回归:导出的 Bash 函数在经过另一个过程后丢失

Regression: Exported Bash function lost after going through another process

从 Ubuntu 14.04 迁移到 16.04 时,我注意到我的几个 Bash 脚本由于缺少导出函数而失败。我想知道这是否与 Shellshock bug 的修复有关,即使我只是 export -f 函数,而不依赖于 Bash- 内部函数表示。失败不会发生在直接 Bash subshell 中,只有在中间有另一个进程时才会发生。例如,Bash 调用 awk / Perl / Vim 调用另一个 Bash。这是一个 Perl 示例:

$ foo() { echo "foobar"; }
$ export -f foo
$ export -f; foo
foo ()
{
    echo "foobar"
}
declare -fx foo
foobar
$ bash -c "export -f; foo"
foo ()
{
    echo "foobar"
}
declare -fx foo
foobar
$ perl -e 'system("bash -c \"export -f; foo\"")'
foo ()
{
    echo "foobar"
}
declare -fx foo
foobar
$ echo $BASH_VERSION
4.3.11(1)-release

不好

$ foo() { echo "foobar"; }
$ export -f foo
$ export -f; foo
foo ()
{
    echo "foobar"
}
declare -fx foo
foobar
$ bash -c "export -f; foo"
foo ()
{
    echo "foobar"
}
declare -fx foo
foobar
$ perl -e 'system("bash -c \"export -f; foo\"")'
bash: foo: command not found
$ echo $BASH_VERSION
4.3.42(1)-release

我是不是做错了什么,或者这是一个错误?

Edit:@chepner 指出 Bash 使用特别命名的 shell 标识符来存储函数。通过 dash(0.5.8-2.1ubuntu2,使用 0.5.7-4ubuntu1)时,这些标识符将被删除。使用 ksh,它们可以存活。我检查了

$ dash
$ sudo strings /proc/$$/environ | grep foo # Still passed from Bash to Dash
BASH_FUNC_foo%%=() {  echo "foobar"
$ bash
$ sudo strings /proc/$$/environ | grep foo # But went missing from Dash to Bash
$ exit
$ exit
$ ksh
$ sudo strings /proc/$$/environ | grep foo
BASH_FUNC_foo%%=() {  echo "foobar"
$ bash
$ sudo strings /proc/$$/environ | grep foo # Kept from Ksh to Bash
BASH_FUNC_foo%%=() {  echo "foobar"

同样,Vim 的行为可以通过 :set shell=/bin/bash / :set shell=/bin/ksh

改变

那么,dash 是罪魁祸首吗?!

TL;DR:已知 dash 问题;灰色区域,可能已修复;最好不要依赖 non-bash parent.

中的出口

这是由于dash 0.5.8的变化引起的; cp。 dash removes exported bash functions from the environment.

是否会修复此问题尚无共识。 POSIX 似乎允许删除无效的环境条目,其他(更晦涩)shells apparent 也这样做,但它会在各种应用程序中引起问题,特别是因为 /bin/sh 在 Ubuntu.

中符号链接到 dash(因此默认为 shell)

我的个人用例是一些简短的实用程序函数,我已将其放入我的 ~/.profile,并在一些 shell 脚本中引用。其中一个在自动启动的 Conky 守护程序中运行,并且那个错过了功能,因为自动启动是通过 dash 发生的。我可以解决这个问题。来自 Korn shell 的 FPATH 自动加载机制在 Bash 中也很好......

这并不理想,但您可以在 Dash 中定义您的函数:

$ foo() { echo "foobar"; }
$ dash -c "$(declare -f); foo"
foobar