Hello, please consider the following code:
bash -c ' a() { local X=2 declare -p X b } b() { local X=3 unset -v X echo "${X-unset}" printenv X || echo Not in env } X=1 a' That gives: declare -- X="2" unset Not in env as expected. Now call the same command with X=0 in the environment, and you get: declare -x -- X="2" unset 2 That is, "unset" confirms it was successfully unset. However, printenv still received a X=2 in its environment as if X was still set and exported, revealing the value of the local variable of the parent scope (which inherited the export attribute) If changed to: X=0 bash -c ' a() { local +x X=2 declare -p X b } b() { local X=3 unset -v X echo "${X-unset}" printenv X || echo Not in env } X=1 a' That gives: declare -- X="2" unset 1 This time it's the value of the variable two levels up the stack that is leaked to the environment (while parameter expansions still confirm the variable is unset). Reproduced with 5.2.21(1)-release and current git HEAD. Setting the localvar_unset option doesn't seem to change the behaviour. In all of the above, I'd expect printenv not to find that variable that has just been unset. The doc states: > The ‘unset’ builtin also acts using the same dynamic scope: if > a variable is local to the current scope, ‘unset’ unsets it; > otherwise the unset will refer to the variable found in any > calling scope as described above. If a variable at the > current local scope is unset, it remains so (appearing as > unset) until it is reset in that scope or until the function > returns. Once the function returns, any instance of the > variable at a previous scope becomes visible. But says nothing about variables from upper scopes still being kept in the environment. In the Environment section: > The environment inherited by any executed command consists of > the shell's initial environment, whose values may be modified > in the shell, less any pairs removed by the ‘unset’ and > ‘export -n’ commands, plus any additions via the ‘export’ and > ‘declare -x’ commands. -- Stephane