2024-11-04 23:57:54 +0100, Steffen Nurpmeso via austin-group-l at The Open
Group:
[...]
> If you mean set(1) getting an option to explicitly control
> *which* local there is, then that is cool .. and maybe the best of
> all options. Indeed. Also because people then can do things like
>
> (set -o tabcomplete) >/dev/null 2>&1 && set -o tabcomplete
[...]
As mentioned earlier in this thread, bash has already introduced
the localvar_inherit and localvar_unset options (though in the
set managed by shopt, not the one managed by set -o).
localvar_inherit decides between Almquist/bosh (inherit) vs
Korn/rest-of-the-world (initially unset), localvar_unset decides
between most vs mksh/yash on what to do when unset is called on
local variables.
Two antagonist concepts that is the biggest blocker to
specifying local ATM.
$ mksh -c 'a=1; f() { local a=2; unset a; echo "${a-unset}"; }; f'
1
$ yash -c 'a=1; f() { local a=2; unset a; echo "${a-unset}"; }; f'
1
In those shells, unset doesn't unset but peels one layer of
local. In bash, that manifests only in:
$ bash -c 'myunset() { unset "$@"; }; a=1; f() { local a=2; myunset a; echo
"${a-unset}"; }; f'
1
When unset is called in a child scope. Otherwise:
$ bash -c 'a=1; f() { local a=2; unset a; echo "${a-unset}"; }; f'
unset
$ bash -O localvar_unset -c 'myunset() { unset "$@"; }; a=1; f() { local a=2;
myunset a; echo "${a-unset}"; }; f'
unset
Where localvar_unset "fixes" that behaviour (which unfortunately
some people have come to rely upon to implement some form of
"upvar" so may be too late for bash to under at least when not
in posix mode)
ATM if you want a local variable that is initially unset, you're
stuffed as:
f() { local var; }
Doesn't make it initially unset in ash-based shells or bosh and
f() { local var; unset var; }
Doesn't work in mksh/yash.
IIRC, the mksh maintainer said he wouldn't change that (I don't
know about yash's maintainer who was not involved in the past
discussions).
POSIX could specify the localvar_inherit and localvar_unset
options, but for the latter that would likely mean that the
behaviour when localvar_unset is not on would have to be left
unspecified because:
1. It differs between mksh/yash and bash
2. IMO, it's an undesirable behaviour¹ that would not be worth
implementing let alone specifying, we'd only be introducing
that option to account for existing implementation behaviour
which they're not willing to change.
For localvar_inherit, it's not perfect either because there
are some shells which are in the don't-inherit category in that
they don't inherit the value but either inherit the attributes,
or don't mask the variable marked for export in the parent
scope.
e.g:
$ mksh -c 'export a=1; f() { local a=2; printenv a; }; f'
1
$ yash -c 'export a=1; f() { local a=2; printenv a; }; f'
1
$ bash -c 'export a=1; f() { local a=2; printenv a; }; f'
2
$ bash -O localvar_inherit -c 'export a=1; f() { local a=2; printenv a; }; f'
2
$ bash +O localvar_inherit -c 'export a=1; f() { local a=2; printenv a; }; f'
2
$ dash -c 'export a=1; f() { local a=2; printenv a; }; f'
2
$ bosh -c 'export a=1; f() { local a=2; printenv a; }; f'
2
$ ksh -c 'export a=1; function f { typeset a=2; printenv a; }; f'
$ zsh -c 'export a=1; f() { local a=2; printenv a; }; f'
$ netbsd-sh -c 'export a=1; f() { local -N a=2; printenv a; }; f'
$
(I don't have access to a ksh88 to test that on ATM)
To me, ksh/zsh (and NetBSD sh with -N) are the ones which truely
don't inherit. dash (like NetBSD's sh (without -N) on which it
is based) inherits so it's expected there.
2024-11-04 23:54:20 +0100, Steffen Nurpmeso via austin-group-l at The Open
Group:
[...]
> I see however people jumping of joy they are enabled to write
> "local --inherit" or "local --scope" or "local --reallylocal" or
> so. Or even the self-describing "local -i" because long options
> are not POSIX, and "local inherit" it cannot be either.
That doesn't fill me with joy either but as mentioned earlier,
there's already a precedent with NetBSD sh implementing local -N
and local -I (-i, -u and -U are already taken in some shells) as
a good will gesture to try and move things forward.
And that means new syntax for all shells which means one where
all implementers can agree on a behaviour (the one in NetBSD
looks fine to me and AFAICT is in agreement with the rough
specification I suggested previously).
--
Stephane
¹ I'd even go as far as saying that an unset that doesn't unset is a bug.