Re: how is locale/unset intended to work with dash?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



2022-02-20 05:15:23 +0100, Christoph Anton Mitterer:
[...]
> I'm trying to understand how local/unset works with dash... and it
> seems to be a bit dangerous.
[...]

AFAIK, it's the Almquist shell that first introduced the "local"
builtin. It was later added to bash¹, zsh, ksh, pdksh, yash, bosh
etc but with semantics slightly different between all shells.

See
https://unix.stackexchange.com/questions/493729/list-of-shells-that-support-local-keyword-for-defining-local-variables/493743#493743
for details.

In the Almquist shell, "local" just makes the variable local to
the function, it doesn't change its value nor attributes. Upon
returning from the function, its original value and attributes
from before invoking "local" are restored.

That's one of the most consistent APIs among shells that have a
local builtin/keyword. IIRC, that's also the API bosh chose.

It can be surprising and dangerous if you're used to the
ksh/bash semantic whereby a new variable is instanciated (though
possibly with some attributes inherited from the original
variable) initially unset, upon the first invocation of "local"
in a given scope.

But in ash-based shells such as dash, you can do:

   local var
   unset -v var

If you want the local variable to be initially unset.

That approach doesn't work in some other shells such as mksh
where unset -v would not unset the variable but peel one level
of local'ness (reveal the variable from the parent scope if
any).

That was discussed extensively on the POSIX (austin-group-l)
mailing list a few years back. The maintainer of NetBSD sh (also
based on ash, and IIRC on which dash is based on), had suggested
adding some -I (inherit, default) and -N (null / unset) options
to "local" so you can decide which behaviour you want in the
hope that other shells would follow so POSIX can eventually
specify a "local" keyword for sh that satisfies existing
implementations.

I'm not aware that any other shell followed that. bash came up
with an alternative mechanism instead: the localvar_inherit
options which affects all "local" invocations.

So:

  bash -O localvar_inherit -O localvar_unset

(localvar_unset fixing a misfeature of bash whereby unset could
also fail to unset in some conditions like in mksh).

should behave similarly to ash-based shells such as dash.

With that clarified, commenting on your post:

> In bash, e.g., it seems to work like that:
> always the var from the next outer scope is used, when
> setting/expanding/unsetting
> 
> This implies, that bash "shadows" variables, e.g.
> 
> $ x=Vtop
> $ f1() { local x=V1 ; f2; echo f1 $x ; }
> $ f2() { echo f2 $x ; unset -v x ; echo f2 $x ; }
> $ echo top $x
> $ f1
> $ echo top $x
> 
> would give this in bash:
> top Vtop
> f2 V1
> f2 Vtop
> f1 Vtop
> top Vtop
> 
> In other words, as soon as x is unset in f2, the f1()'s local x ceases
> to exist and the global x is used.

That's the misfeature I was talking about: when unset is called
from a function where the variable has not been defined, it
doesn't unset the variable but reveals the variable from the
parent scope like mksh does.

You can do shopt -s localvar_unset for unset to always unset
(without undoing unshadowing the variable) like in ksh/zsh.

[...]
> However,... things can get far more strange:
> $ x=Vtop
> $ f1() { local x=V1 ; f2 ; echo f1 $x; }
> $ f2() { echo f2 $x; unset -v x; echo f2 $x; f3 ; echo f2 $x; }
> $ f3() { echo f3 $x; unset -v x; echo f3 $x; }
> $ echo top $x
> vf1

???

> $ echo top $x

???
 
> which gives in dash:
> 
> top Vtop
> f2 V1
> f2
> f3
> f3
> f2
> f1
> top Vtop   <- shouldn't that have been unset by the f3’s unset?
>               f1’s x was already gone by then

No, x was made local by f1. No amount of unsetting from within
that invocation, even if it's in subfunctions will undo that, it
still unsets (removes a value from) the local variable of f1.
.
The variable is local, that's the whole point, you don't want
whatever f1 does to affect the "x" variable of the parent scope
(which is why I call bash/mksh's behaviour a misfeature).

When f1 returns, the value and attributes of x are restored from
when before "local x" was invoked 

[...]
> => when the unset is in the *same* scope level as the `local` ... it
> doesn't "unshadow".

Yes, which is why it's not as bad as in mksh IMO.

---------------
¹ very shortly after in the case of bash, suggesting local was
possibly added to ash and bash independently from each other.
local in bash was an alias for typeset copied from ksh

-- 
Stephane



[Index of Archives]     [LARTC]     [Bugtraq]     [Yosemite Forum]     [Photo]

  Powered by Linux