0

I need help with bash expansion.
I want to retrieve array values, GNU bash 5.1.0. The array name shall be a variable. "Just" referencing a variable in bash.

I have an array named "armin" and its name is in variable $gral (works fine):

gral="armin"

Values are assigned:

declare -a ${gral}[1]="milk"
declare -a ${gral}[2]="cow"
declare  ${gral}[7]="budgie"
declare  ${gral}[9]="pla9ne"

fine.

Array exists:

$ echo ${armin[@]}
milk cow budgie pla9ne

Array index exists:

$echo ${!armin[@]}
1 2 7 9

Array and index are fine.

I want to retrieve the array by referencing its name as a variable, not manually. Have plenty of them ...
The variable was set and used before:

$ echo $gral
armin  ## name of our bash array

fine - so far.

Just to show the difference NOT using a variable:

echo ${armin[@]}
milk cow budgie pla9ne

Now attempts to reference a variable (gral) to call the name (armin):

$ echo ${$gral[@]}
-bash: ${$gral[@]}: wrong substitution.

$echo ${"$gral"[@]}
-bash: ${"$gral"[@]}: wrong substitution.
echo ${"gral"[@]}
-bash: ${"gral"[@]}: wrong substitution.
echo ${${gral}[@]}
-bash: ${${gral}[@]}: wrong substitution.

all fail. Some attempts with "eval" as well. Using associative (declare -A) makes no difference.

Rem.: Index works fine this way, no issue. Name is the issue.

I think I am missing something. Maybe the answer was described before, I found a lot of interesting stuff about variables in arrays but did not recognize an answer to my challenge.

Can you please help me find the term to retrieve the array by referencing its name as a variable?

4

Use namerefs (in Bash >= 4.3):

$ armin=(foo bar doo)
$ declare -n gral=armin      # 'gral' references array 'armin'  
$ gral[123]=quux             # same as 'armin[123]=quux'
$ echo "${gral[@]}"
foo bar doo quux
$ echo "${gral[1]}"
bar
$ echo "${!gral[@]}"         # listing the indexes works too
0 1 2 123

See also: Does bash provide support for using pointers?

3
  • Thanks @ilkkachu this is interesting and elegant. It requires an additional intermediate step (other than doing it with eval ) but its clear and comprehensible. Thanks a lot. This is one of 3 different valid solutions. Mar 13 at 0:47
  • @opinion_no9, hmm, I'm not sure what you mean. The only step that changes from your initial code is marking gral as a nameref. Well, that does mean the name has to be assigned to a variable, you can't use e.g. the output of an expansion directly as a nameref, but I have a hard time figuring a situation where you'd want to. And in any case, it's only one line.
    – ilkkachu
    Mar 13 at 9:19
  • actually the other solutions do not work if I (extending the example) want to retrieve the "index" or subscript the same way as the members of the index. This is actually the best solution. Thank you for your appreciated and succesful help. Mar 14 at 1:02
1

As @ilkkachu answers, namerefs are the tool to use here. They make it super easy to pass arrays to functions. For example:

dumpArray() {
  local -n ary=$1
  for i in "${!ary[@]}"; do
    printf "%s\t%s\n" "$i" "${ary[$i]}"
  done
}

That function can handle arrays and associative arrays:

$ declare -a armin=([1]=milk [2]=cow [7]=bugle [9]=pla9ne)

$ dumpArray armin
1   milk
2   cow
7   bugle
9   pla9ne

$ declare -A map=([foo]=bar [baz]=qux)

$ dumpArray map
foo bar
baz qux

The only real problem is you that it's messy to make a nameref reference an array with the same name:

$ ary=(a b c)

$ dumpArray ary
bash: local: warning: ary: circular name reference
bash: warning: ary: circular name reference
bash: warning: ary: circular name reference
bash: warning: ary: circular name reference
bash: warning: ary: circular name reference
0   a
bash: warning: ary: circular name reference
bash: warning: ary: circular name reference
1   b
bash: warning: ary: circular name reference
bash: warning: ary: circular name reference
2   c

So in the function, you make the array have an odd name, like

dumpArray() {
  local -n __dumpArray_ary=$1
  do_stuff_with "${__dumpArray_ary[@]}"
}
1

Include the "index", i.e. the [@], in the variable that contains the name:

ref=$gral'[@]'
printf '%s\n' "${!ref}"

Output:

milk
cow
budgie
pla9ne

It works with values containing spaces, too.

3
  • no, it is not about index. If I did understand your reply right it is not the solution. Index is already solved in the question. Thanks anyways. Mar 12 at 16:39
  • @opinion_no9: I mean @ by the "index" here.
    – choroba
    Mar 12 at 16:45
  • I like your solution - Elegant and concise. Wonder why I missed it. Thanks! The additional intermediate step can be done different way but it is unavoidable. This is one of 3 different valid solutions. However, I meanwhile found a solution without intermediate step using eval but it is not better in terms of security or elegance. You got my upvote ;-) Mar 13 at 0:48
-1

well,
the answer to dereferecing an array by a name passed as variable (partly resembles pointer)
is (example above re-used):

eval echo "\$$(eval echo "{$gral[@]}")"
with $gral holding the array name

Result:

milk cow budgie pla9ne

You may extend this solution now to iterate bash array (indexed AND associated) content using the index entries.

Glad I found the answer. Thank you all for your help!

Accessing the contents of the contents of a variable.

There are hints leading to the result in abs-guide:
https://tldp.org/LDP/abs/html/abs-guide.html#IVR

Committed thanks to Mendel Cooper; so thankful we have this great gift named Advanced Bash-Scripting Guide.

OK,
eval is ugly due to security concerns.
Try to find a better one ...

1
  • someone really found it appropriate to downvote the proper solution ... "great!" Mar 12 at 23:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.