On 03/17/2017 07:21 PM, Stephane Chazelas wrote: > I don't expect the need to have to add "local var" in > > ( > unset -v var > echo "${var-OK}" > )
True. I would pretty much never use a subshell command group when I know that locals are available though. And if I know locals are available then (except dash) I know arrays are available, in which case I'd almost never use field splitting. This is only like the millionth screwy gotcha with IFS. Everybody knows IFS is broken beyond repair :o) Then again, you could easily write a similar bug with any other special variable that has a side-effect. > would be obvious to many people beside you though. > very > People writing function libraries meant to be used by several > POSIX-like shells need to change their code to: > > split() ( > [ -z "$BASH_VERSION" ] || local IFS # WA for bash bug^Wmisfeature > unset -v IFS > set -f > split+glob $1 > ) > > if they want them to be reliable in bash. Even if the inconsistent effect of unset isn't obvious, it should be obvious that a subshell isn't equivalent to setting a local, because it doesn't just make dynamic scope go away. I'm far more surprised by the behavior of mksh and dash, in which it's the subshell rather than the unset builtin that's inconsistent. Why would a subshell just make the call stack go away? That makes no sense, and a subshell isn't supposed to do that. Dash and mksh don't even agree with one another on how that works: (cmd) ~ $ mksh /dev/fd/3 3<<\EOF function f { typeset x=f; g; } function g { ( unset x; echo "${x-unset}"; ) } x=global; f EOF global (ins) ~ $ dash /dev/fd/3 3<<\EOF alias typeset=local function= function f() { typeset x=f; g; } function g() { ( unset x; echo "${x-unset}"; ) } x=global; f EOF unset In fact, mksh prints "global" even without the subshell, despite it using dynamic scope for either function definition syntax. At least bash's output in this case (empty) can be fully explained as a combination of quirks with unset and hidden locals (neither being documented), plus dynamic scope being what it is. We're pretty much arguing over which is the less counter-intuitive inconsistency here. If mksh's subshells worked consistently as in bash, you'd have written the same bug as bash in your example. And it would be even easier to do so without the unset quirk since this could happen within a single function call too: (cmd) ~ $ mksh /dev/fd/3 3<<\EOF function f { typeset x=f ( unset x; echo "${x-unset}"; ) } x=global; f EOF global This could really bite if x and f are defined in separate files so the initial state of x isn't necessarily known. > So what should the documentation be? With my "eval" case in > mind, it's hard to explain without getting down to how stacking > variables work. Maybe something like: > > [...] All that touches on several issues in addition to scope, such as the various states that a variable can be in, and the exact nature of references to variables like 'a[@]'. That's some of the least-well documented stuff, but some of that should also probably be left subject to change due to the great inconsistency across shells and other issues just within bash. (Ugh also have to mention the stupid 'a[0]' with associative arrays - that's one where "consistency" is itself a bug). > It might be worth pointing out that "unset -v", contrary to the > default behaviour, won't unset functions so it's a good idea to > use "unset -v" instead of "unset" if one can't guarantee that > the variable was set beforehand (like the common case of using > unset to remove a variable which was potentially imported from > the environment). Yeah. I believe POSIX mentions that as well.