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

时间:2016-06-28 15:05:47

标签: bash dash-shell shellshock-bash-bug

从Ubuntu 14.04迁移到16.04时,我注意到由于缺少导出的功能,我的几个Bash脚本失败了。我想知道这是否与Shellshock bug的修复有关,即使我只是export -f函数,而不是依赖于Bash内部函数表示。只有在中间存在另一个进程时,才会在直接Bash子shell中发生故障。例如,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

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

编辑:@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"

同样,可以通过:set shell=/bin/bash / :set shell=/bin/ksh

更改Vim的行为

那么,dash应该受到指责吗?!

2 个答案:

答案 0 :(得分:4)

TL; DR:已知dash问题;灰色区域,可能是固定的;最好不要依赖非bash父母存活的出口。

这是由破折号0.5.8的变化引起的; CP。 dash removes exported bash functions from the environment

目前还没有达成共识,是否会解决这个问题。 POSIX似乎允许剥离无效的环境条目,其他(更加模糊的)shell显然也会这样做,但它会导致各种应用程序出现问题,特别是因为/bin/sh符号链接到dash(因此Ubuntu中的默认shell。

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

答案 1 :(得分:1)

这不太理想,但你可以在Dash中定义你的功能:

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