bug-bash
[Top][All Lists]
Advanced

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

Re: Unset array doesn't work


From: Robert Elz
Subject: Re: Unset array doesn't work
Date: Sat, 03 Mar 2018 14:23:47 +0700

    Date:        Fri, 2 Mar 2018 14:43:02 -0500
    From:        Chet Ramey <chet.ramey@case.edu>
    Message-ID:  <4502a0e5-0600-d294-9af2-4e9eeb0a058e@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 correctly behave that way.   In bash (and in
several other shells, though not all, due to unrelated differences)
the same is true of the local command in a function...

var=set
func() { local var; echo ${var-unset}; }
echo $var ; func; echo $var

prints "set" "unset" "set"  showing that in the function there is
a var (which must still exist as its value is retained in the global
scope) which is unset.

Even if the model was that the "var" in the function is something
completely unrelated to the global "var" (as it would be in C for
example) we can still see that the local command must have
created some state for it, as otherwise the echo would be
referring to the global "var".

This can be more obviously seen in

var=set
func() { [ -n "$1" ] && local var; echo ${var-unset}; }
echo $var ; func x; func; echo $var

The same code either accesses the local or global var
depending upon whether we happened to have executed
the local command or not - indicating that command sets
some atttibute, and still makes an unset var.

Again this is all as it should be (given the assumption that "local"
creates an unset version of the variable, which is a rational choice,
if not the one made by all shells - a topic we have discussed in
another context.)

The best model for all of this is that set/unset is an atttibute of a
shell variable, just like exported, read-only (and any more that
exist in various shells) and that when we reference a variable
which does not (or perhaps even just did not) exist at all, then
it is made to appear as if it were an unset variable, and this is
the special case, rather than considering leaving some state
around to remember attributes of vars that happen to be unset
but must continue to exist as being what is unusual or special.

An analogy is the unlink sys call, which is not "remove".   If you
think of it as remove, and that its function is to remove files, then
things get way too complex to describe the behaviour.   But if
it just alters an attribute of the file (in that case, its link/ref count)
it all becomes simple.   And then when there is no longer any
way to access the file, it can be removed as a space saving
optimisation.     Treat the unset command in the shell the same
way.  If, after it, there is nothing about the variable that is different
than a variable which does not exist, then we can remove it
because that changes nothing, and saves memory  - but if there
remains anything about the variable which is still needed, then
we just cause the unset atttibute to be set (however that is
implemented) and continue.

kre




reply via email to

[Prev in Thread] Current Thread [Next in Thread]