How to use a bash variable reference to an associative array in a bash function without declaring it before calling that function?

Viewed 155

There are multiple ways to get a result from a function in a bash script, one is to use reference variables like local -n out_ref="$1, which is also my preferred way.

My bash version is:

GNU bash, Version 5.0.3(1)-release 

Recently, one of my bash functions needed to produce a associative array as a result, like in this example code:

#!/bin/bash

testFunction() {
    local -n out_ref="$1"

    out_ref[name]="Fry"
    out_ref[company]="Planet Express"
}

declare -A employee
testFunction employee

echo -e "employee[name]: ${employee[name]}"
echo -e "employee[company]: ${employee[company]}"

I declare the variable employee as an associative array with declare -A.

The output is:

employee[name]: Fry
employee[company]: Planet Express

If I remove the line declare -A employee, the output is:

employee[name]: Planet Express
employee[company]: Planet Express

Is there a way to move the declaration of the associative array into the function, so the user of that function does not need to do it beforehand?

3 Answers

Use declare -Ag "$1" inside the function, so that you declare employee as a global variable.

Without declare -A employee, employee is basically treated like an ordinary indexed array. In that context, name and employee are both undefined variables evaluated in an arithmetic context, so both evaluate to 0.

I'm not entirely sure how local -n is implemented (local -nA is not allowed to mark the reference as an associative array), but it appears that lacking a "real" associate array to assign to, the value of employee is just whatever the last value assigned to any key of the reference out_ref. (Probably the same as above: both name and company evaluate to 0 in the arithmetic context, so you are assigning to out_ref[0] twice (and because out_ref does not refer to an array-marked name, you are just assigning to out_ref itself.)

Expanding on what these better minds have provided, here's a functioning example that shows it in actual use.

A() {
  declare -Ag "$1"
  local -n ref="$1"
  ref[A]='in A'
}

B() {
  declare -Ag "$1"
  local -n ref="$1"
  ref[B]='in B'
}

A foo
B foo
declare -p foo

Saved and executed as a file named tst, here's the run and the output -

$: ./tst
declare -A foo=([A]="in A" [B]="in B" )

Note the "redeclaration" of the global variable in multiple functions does not impugn the data integrity.

Related