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 c...@case.edu http://tiswww.cwru.edu/~chet/