Regression: Exported Bash function lost after goin

2020-03-24 07:46发布

问题:

When moving from Ubuntu 14.04 to 16.04, I've noticed several of my Bash scripts failing due to missing exported functions. I wonder whether this is related to the fixes for the Shellshock bug, even though I simply export -f the functions, and not relying on the Bash-internal function representation. The failure does not occur in a direct Bash subshell, only if there's another process in between. For example, Bash invoking awk / Perl / Vim invoking another Bash. Here's an example with Perl:

Good

$ 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

Bad

$ 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

Am I doing something wrong, or is this a bug?

Edit: @chepner pointed out that Bash uses specially-named shell identifiers to store the functions. When going through dash (0.5.8-2.1ubuntu2, working with 0.5.7-4ubuntu1), these identifiers are removed. With ksh, they are kept alive. I checked with

$ 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"

Likewise, the behavior of Vim can be changed via :set shell=/bin/bash / :set shell=/bin/ksh

So, is dash to blame?!

回答1:

TL;DR: Known dash problem; gray area, might be fixed; better not rely on exports surviving a non-bash parent.

This is caused by a change in dash 0.5.8; cp. dash removes exported bash functions from the environment.

There is no consensus yet whether this will be fixed. POSIX seems to allow the stripping of invalid environment entries, other (more obscure) shells apparently do this as well, but it causes problems in various applications, especially because /bin/sh is symlinked to dash (and therefore the default shell) in Ubuntu.


My personal use case is some short utility functions that I've put in my ~/.profile, and which I reference in some shell scripts. One of those runs in an autostarted Conky daemon, and that one misses the functions because the autostart happens through dash. I can work around this. The FPATH autoload mechanism from Korn shell would be nice to have in Bash, too...



回答2:

This is not ideal, but you can define your functions inside of Dash:

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