79668250

Date: 2025-06-16 21:18:14
Score: 1
Natty:
Report link

For whoever struggles with similar issues with Bash name references, the good starting point is Greg's wiki:

Name reference variables are the preferred method for performing variable indirection. Older versions of Bash could also use a ! prefix operator in parameter expansions for variable indirection. Namerefs should be used unless portability to older bash versions is required. No other shell uses ${!variable} for indirection and there are problems relating to use of that syntax for this purpose. It is also less flexible.

With that, it becomes important to understand the dynamic scoping of Bash (thanks @jhnc) which is not particularly emphasized, but (qouting the same):

Indirection can only be achieved by indirectly evaluating variable names. IOW, you can never have a real unambiguous reference to an object in memory; the best you can do is use the name of a variable to try simulating the effect. Therefore, you must control the value of the ref and ensure side-effects such as globbing, user-input, and conflicting local parameters can't affect parameter names. Names must either be deterministic or validated in a way that makes certain guarantees. If an end user can populate the ref variable with arbitrary strings, the result can be unexpected code injection.

And importantly on main Bash page of Greg's wiki:

Name references are created with declare -n, and they are local variables with local names. Any reference to the variable by its local name triggers a search for a variable with the name of its content. This uses the same dynamic scope rules as normal variables. So, the obvious issues apply: the local name and the referenced name must be different. The referenced name should also not be a local variable of the function in which the nameref is being used.

The workaround for this is to make every local variable in the function (not just the nameref) have a name that the caller is unlikely to use.

Further, from Bash manual:

Local variables "shadow" variables with the same name declared at previous scopes. For instance, a local variable declared in a function hides a global variable of the same name: references and assignments refer to the local variable, leaving the global variable unmodified. When the function returns, the global variable is once again visible.

The shell uses dynamic scoping to control a variable’s visibility within functions. With dynamic scoping, visible variables and their values are a result of the sequence of function calls that caused execution to reach the current function. The value of a variable that a function sees depends on its value within its caller, if any, whether that caller is the "global" scope or another shell function. This is also the value that a local variable declaration "shadows", and the value that is restored when the function returns.

For example, if a variable var is declared as local in function func1, and func1 calls another function func2, references to var made from within func2 will resolve to the local variable var from func1, shadowing any global variable named var.

And to top this all off, I had quite a roller coaster ride with declare and its switches.

The issue is that it appears it's setting or unsetting variable attributes, but not all switches are equal in that sense:

Using ‘+’ instead of ‘-’ turns off the attribute instead, with the exceptions that ‘+a’ and ‘+A’ may not be used to destroy array variables and ‘+r’ will not remove the readonly attribute. When used in a function, declare makes each name local, as with the local command, unless the -g option is used. If a variable name is followed by =value, the value of the variable is set to value.

One option that stands out is -g that really is NOT an attribute and therefore has to be repeated every time when manipulating other attributes or otherwise one is operating on a differently (scoped) variable.

Another less logical is -n which has confusing documentation:

-n Give each name the nameref attribute, making it a name reference to another variable. That other variable is defined by the value of name. All references, assignments, and attribute modifications to name, except for those using or changing the -n attribute itself, are performed on the variable referenced by name’s value. The nameref attribute cannot be applied to array variables.

This attribute also "disappears" when not reused, e.g. one cannot global_array=(x y z); f() { declare -n refarray; refarray=global_array; declare -r refarray; echo "${refarray[*]}"; }; f as the second -r loses one the array, it would need to go again with -rn.

Reasons:
  • Blacklisted phrase (0.5): thanks
  • Long answer (-1):
  • Has code block (-0.5):
  • User mentioned (1): @jhnc
  • Self-answer (0.5):
  • Low reputation (0.5):
Posted by: Albert Camu