Search code examples
basheval

Why local variable is not assigned to a non-local variable with the same name?


Scenario:

$ cat t426.sh
set -x
f()
{
    local x="x"
    eval $1=$x
}

x="y"

f x

echo $x

$ bash t426.sh
+ x=y
+ f x
+ local x=x
+ eval x=x
++ x=x
+ echo y
y

Here we see that y is printed. I expect that x is printed.

If I change the name of the local variable from x to x_, then x is printed.

Why local variable is not assigned to a non-local variable with the same name?

How to fix, so that local variable has the same name as a non-local variable?


Solution

  • Assumptions:

    • f() input is the name of a global variable
    • f() resets the value of the global variable (in this case always setting to the string 'x')

    With bash 4.3+ we have access to the nameref feature which functions like a symlink to a (global) variable.

    $ cat myscript
    #!/bin/bash
    
    f() {
        local -n _var=$1     # -n == nameref
        _var="x"
    }
    
    a=3
    b="y"
    
    echo "### before:"
    typeset -p a b
    
    f a
    f b
    
    echo "### after:"
    typeset -p a b
    

    Addressing OP's issue - local variable has the same name as a non-local variable - instead of a local variable with a constantly changing name (to match the global variable), you have a statically named local variable (_var) which acts as a symlink to the global variable.

    Taking for a test drive:

    $ ./myscript
    ### before:
    declare -- a="3"
    declare -- b="y"
    ### after:
    declare -- a="x"
    declare -- b="x"