Re: Unset array doesn't work
Hi everyone, I'm not a member of Bash developers, but I would like to leave comments as one of Bash users. -- I don't agree with changing the default behavior that removes the placeholder of previous-context variables. The reason is just backward compatibility, but it's important. For example, there is actually a widely-used technique, "local $1 && upvar $1", relying on the behavior of "unset" that removes the placeholder of the previous-context variable. Everyone who has even once looked inside "bash-completion" implementation should know this because bash-completion framework extensively uses this technique. Usually "bash-completion" is defaultly loaded in major Linux distributions, and changing the default behavior will break the completion in such wide environment. Also the change will break all the other scripts that uses "local && upvar" technique. If "unset" doesn't remove placeholders, there is no alternative ways to do the similar thing unless some new builtin "unlocal" or something is introduced. For another example, the behavior of "unset" can be used to get the value of hidden global variables, leaving the hiding local variables, by repeatedly calling "unset" in a subshell. As far as I know, there is no other way to get the value of hidden global variables although the value can be easily set by "declare -g". In fact I use both techniques in my scripts. In addition I won't be surprised if there is other useful techniques that uses the current behavior of "unset". -- I agree with that the current context-dependent behavior of "unset" is the source of confusion as I happened to know this behavior just two years ago. At least, I believe, this context-dependent behavior should be written somewhere in the documents. If a new shell option that resolves the context dependence will be introduced, I prefer to have a switch that changes the behavior of the local-scope "unset" but not the previous-scope "unset". The reason is the current effects of local-scope "unset" can be satisfied by bash's "local" builtin while previous-scope "unset" have no alternatives. For example, the example provided by kre >myfunc() { >local IFS >unset IFS ># do some code >} is actually equivalent to the following code: >myfunc() { >local IFS ># do some code >} If one wants to make an existing local variable have unset attribute in the local-scope placeholder, one can write as > func() { > local a=1 > # do something > unset a > > local a > # do something > } which should work for both side of the option that switches the local-scope "unset". -- I don't think it's a bug that "unset" removes the placeholder of the previous-context variables. It's just an intentional "design" as is known from Chet and other people's replies. And also I don't see any strong enough reason to conclude that it's a kind of a "design bug". In fact the behavior of shells varies for "unset": Example 1 (unsets previous-context variables): Note: Some shell supports local variables with the "typeset" builtin. For such a shell please replace "local" by "typeset" in the following examples. $ echo $(a=1; u() { unset a; }; f() { local a=2; echo a=$a; u; echo a=$a; }; f; echo a=$a) a=2 a= a=1 # zsh, dash, posh a=2 a=1 a=1 # mksh, yash, bash Example 2 (unsets variables defined in the local scope): $ echo $(a=1; f() { local a=2; echo a=$a; unset a; echo a=$a; }; f; echo a=$a) a=2 a= a=1 # zsh, dash, posh, bash a=2 a=1 a=1 # mksh, yash Versions of shells that I tested with: - bash 4.4.12 - zsh 5.2 - mksh @(#)MIRBSD KSH R54 2016/11/11 - posh 0.12.6 - yash 2.46 - dash 0.5.9 - Note: ksh seems not support local variables. "typeset" in function scopes defines a global variable, and "unset" removes the global variable. - Note: "busybox sh" is a variant of dash, so the behavior is the same. These results means there is no single answer for the "correct design" of unset. The only point that bash is confusing is its context-dependent behavior. -- > var=set > func() { local var; echo ${var-unset}; } > echo $var ; func; echo $var For this, again the result varies from shell to shell, so I think there is no single answer for the "correct design" of "local". Example 3: $ echo $(a=set; f() { typeset a; echo ${a-unset}; }; echo $a; f; echo $a) set set set # dash set set # zsh set unset set # mksh, bash, posh, yash -- Best regards, Koichi 2018-03-03 16:23 GMT+09:00 Robert Elz : > Date:Fri, 2 Mar 2018 14:43:02 -0500 > From:Chet Ramey > Message-ID: <4502a0e5-0600-d294-9af2-4e9eeb0a0...@case.edu> > > My final comments on this subject: > > | Perhaps. But bash has never done this. Not from day one. That's 30 years. > > That a bug (be it a design bug, or a coding bug) has existed a long tiime > does not make it any less a bug. > > I have been using bas
Re: Unset array doesn't work
Robert Elz: > And yet when that change to the entrenched behaviour was made, > there were no complaints? And there's no option to switch back to > the previous way? Kind of suggests just how important everyone > believes the original method was, doesn't it? doesn't the same argument apply even more strongly to your proposed change? from the fact that the "bug" has been in existence for decades without a huge clamor for it to be "fixed", your own reasoning would imply that "fixing" it can't be that important. Koichi Murase: > I don't agree with changing the default behavior that removes the > placeholder of previous-context variables. The reason is just backward > compatibility, but it's important. speaking as a mere bash user, i want to give a huge +1 to Koichi's point. after decades of bash doing unset one way, changing the behavior carries a risk of breaking existing scripts. the upside seems small considering that if people aren't even aware of this behavior, it can't be much of a pain point now can it? but it can become a pain point if bash changes something that people have been relying on, even unknowingly. IMHO, testability should be a primary factor when considering any new bash features. from a testing standpoint there are "too many" shell options already. and it's not just about testing bash itself. how many existing shell scripts are going to break because some user unknowingly ran the script with a combination of options that the script author wasn't aware of? so even if it were easy to implement as a shell option, it doesn't sound like a good idea to me. i might feel differently if bash had comprehensive test coverage. AFAIK the tests are nowhere near strong enough. changing the default setting of the proposed option seems even more risky. On Fri, Mar 2, 2018 at 11:23 PM, Robert Elz wrote: > Date:Fri, 2 Mar 2018 14:43:02 -0500 > From:Chet Ramey > Message-ID: <4502a0e5-0600-d294-9af2-4e9eeb0a0...@case.edu> > > My final comments on this subject: > > | Perhaps. But bash has never done this. Not from day one. That's 30 > years. > > That a bug (be it a design bug, or a coding bug) has existed a long tiime > does not make it any less a bug. > > I have been using bash for essentially all that time (from before you took > over maintainership) and I never knew it worked like that. From comments > here (where some people far more knowledgable about bash and its > internals than I are to be found) I suspect that very few other people know > about it either. > > | This is how bash dynamic scoping works. The exception for the > declaration/ > | unset at the current scope was added 16 years ago, and the existing > | behavior was already entrenched. > > And yet when that change to the entrenched behaviour was made, > there were no complaints? And there's no option to switch back to > the previous way? Kind of suggests just how important everyone > believes the original method was, doesn't it? > > | I can see doing this and allowing it to be toggled by a shell option. > > A suggestion: Do that for bash 5, and in the alpha release, make > the option default to cause things to work the opposite way than > happens now (so the option needs to be explicitly changed to get > the current behaviour). I know that's the opposite of what would > usually be done in order to retain backwards compat, but for this, > I think it would be a useful test to see if anyone notices the difference. > You can always change it for beta/final releases if there are issues. > If not, perhaps the option can just go away (then or later.) > > | > Lastly, where does the notion of "remove" come from? > | > | As a way to describe the historical bash behavior, it works. > > Yes, that I understand. My issue is that I believe this is colouring > your thoughts on just what "unset" is - same as the "appear/be" > (trivial seeming) semantic issue you commented on in another message. > > That is, it appears to me as if you believe that "unset" (as a state, not > the command here) implies "non-existing". That's never been correct. > > The converse is correct - a variable that does not exist appears as > an unset variable when referenced. > > There are (even ignoring the unset command) too many ways > (in bash, as well as other shells) to get variables that patently > obviously "exist" in some form or other but are unset. > > The most obvious example is > > export newvar > > after that > > echo ${newvar-unset} > > prints "unset". Sometime later if we give newvar a value, it, and its > new value are exported - demonstrating that the export attribute was > remembered (ie: "newvar" existed before it was set - it must have done > in order to retain an atttribute). > > jinx$ export newvar > jinx$ echo ${newvar-unset} > unset > jinx$ newvar=set > jinx$ printenv newvar > set > jinx$ echo $BASH_VERSION > 4.4.12(1)-release > > All shells that function correctl
coding standards
admittedly this is a very minor point, but i am curious. this has to do with coding standards for bash source. consider an if statement in C (or bash, for that matter). which is form is better? Form (A): if (flag) X(); else Y(); Form (B): if (flag == 0) Y(); else X(); they are functionally equivalent. but IMHO (A) is slightly more readable. first because flag (in this case) is intended to be a boolean value not arithmetic, and second because it's simpler to think about an if when the condition is positive. this is what i'd say if (B) were under code review. i submitted a patch with code in form (A). it was added to the code base in form (B). was there a good reason for this mutation? NOTE: i'm OK with the fact that alterations were made. but i wonder what was the reasoning? as near as i can tell, my patch could have been just applied verbatim, but it wasn't. and i don't see how the alterations were an improvement. all of which makes me wonder what is the review process for changes to the bash source? here is the actual code from which the above example was derived, in subst.c. my patch (form (A)): -report_error (_("%s: parameter null or not set"), name); +{ + if (check_nullness) + report_error (_("%s: parameter null or not set"), name); + else + report_error (_("%s: parameter is not set"), name); +} the new code (form (B)): else if (check_null == 0) report_error (_("%s: parameter not set"), name); else report_error (_("%s: parameter null or not set"), name); a couple of other minor alterations were made. * my code used the same variable name check_nullness which was used in the calling routine, vs check_null in the altered version. i'm OK with it even though i think using the same variable name to stand for the same thing would be slightly better. * my code's error message was a sentence, "parameter *is* not set" vs the terse "parameter not set". there is a certain consistency to omitting the verb, if you don't care about clarity. i'm OK with this.