bug-bash
[Top][All Lists]
Advanced

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

Re: Variables getting silently unset on redeclaration


From: Chet Ramey
Subject: Re: Variables getting silently unset on redeclaration
Date: Fri, 16 Mar 2018 11:35:05 -0400
User-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:52.0) Gecko/20100101 Thunderbird/52.6.0

On 3/15/18 10:30 PM, dtr@home.homenet wrote:

> Bash Version: 4.4
> Patch Level: 12
> Release Status: release
> 
> Description:
>       Variables mistakenly declared as local and then set as global
>       happen to be unset for the “nounset” option and known 
>       to “declare -p” at the same time. Moreover, whatever value was 
>       assigned to them is lost. 

It's not. You've just encountered dynamic scoping, and the fact that a
variable is not set until it has been assigned a value, even if it has
attributes.

> 
>       Re-declaration doesn’t cause an error, and such variables may cause
>       silent failure, because the error can only be caught on the first
>       use and only in two cases:
>       - nounset option is on;
>       - a test on variable existence [ -v is called and errexit is on.
>       The unset variable isn’t unset for declare, which is confusing.

Oh, but it is.

> 
> Expected behaviour:
>       Variables should either change their type to global and maintain
>       the value assigned to them, or trigger SIGERR on re-declaration, 
>       (and not when they’re caught much later by “errexit” or 
> “nounset”).

This isn't useful or intuitive.

> 
> Repeat-By:

So the call stack (and list of variable scopes) is main -> func -> set_var.
Variable references are satisfied from the current scope or the previous
scope `nearest' the current scope, which may be another local scope.


>       #! /usr/bin/env bash
> 
>       set -eu
>       
>       set_var() {
>               local name="$1" value="$2"
>               # Fixing names
>               name=${name//-/_}
>               name=${name//\//_}
>               # Here should be some work
>               # to retrieve data…
>               declare -g $name="$value"

You've created a variable in the global scope. The local declaration in
`func' still exists, is still unset (because you haven't given it a
value), and satisfies references to my_poor_var due to dynamic scoping
rules.

>               # my_poor_var is already unset here
>               return 0
>       }
> 
>       func() {
>               # mistakenly left among local variables
>               local my_poor_var
>               set_var  my-poor/var  2

You've created a global variable named `my_poor_var' with value 2 whose
existence is shadowed by the local instance with with same name.

> 
>               # (1) 
>               [ -v my_poor_var ]
>               # quits on SIGERR

Reference to an unset variable causes the command to fail. It's debatable
whether this should trigger an exit because of -u being set, but it doesn't
right now.

>               # (2)
>               echo "$my_poor_var"
>               # line 18: my_poor_var: unbound variable

The variable is unset because you haven't given it a value.

>               # (3)
>               declare -p my_poor_var
>               # Reports, that it exists(!), but has no value.

It's unset because it has no value. It exists, because it has a name
and attributes (the local attribute).

>               # Calling declare -p doesn’t make the program quit,
>               # unlike with really unset variables

declare -p exists to say how to recreate a variable with its current
state. If a variable has attributes but no value, it should be possible
to recreate it.

> 
>               return 0
>       }
> 
>       func

If you were to turn off -e and -u, and run `declare -p my_poor_var' after
the call to `func', you would find you have a global variable.

Chet
-- 
``The lyf so short, the craft so long to lerne.'' - Chaucer
                 ``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU    chet@case.edu    http://tiswww.cwru.edu/~chet/



reply via email to

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