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?!
system
is usingdash
instead ofbash
in the new version of Ubuntu, it appearsdash
does not correctly pass its inherited environment to a child process. Compareenv foo-bar=3 dash -c env
withenv foo-bar=3 bash -c env
. In both cases, the shell run by the first call toenv
receives an environment name (foo-bar
) that is not a valid shell identifier.bash
pass it on to theenv
process it runs, butdash
does not. – Pocketbash -> dash -> bash
only works in old Ubuntu. – Prochronismfoo
, the environment string is something likeBASH_FUNC_foo%%=() { echo footer; }
. (Newline instead of;
, though).dash
seems to strip that out of its environment. A process isn't supposed to remove anything from its environment, though, as far as I know. It can ignore it, but it is supposed to pass what it gets on to any of its children. – Pocket