27

Suppose I have some bash arrays:

A1=(apple trees)
A2=(building blocks)
A3=(color television)

And index J=2, how to get the array contents of A2?

2
  • This is very cool. I consider myself a bash expert and I was always under the assumption that bash cannot do array indirection such as ${!ind[@]} but I never thought to introduce a temp var to solve it. Commented Jan 3, 2011 at 5:08
  • You should post your answer as an answer. You will be able to accept it when the time limit expires. Here is an example of the usual way that arrays are used with indirection: d=13; e=24; f=35; a=(d e f); echo ${!a[1]} which results in "24". Commented Jan 3, 2011 at 5:51

3 Answers 3

22

I've already found a resolution, this can be done by:

$ Aref=A$J
$ echo ${!Aref}
building
$ Aref=A$J[1]
$ echo ${!Aref}
blocks
$ Aref=A$J[@]
$ echo "${!Aref}"
building blocks
Sign up to request clarification or add additional context in comments.

8 Comments

How can you assign to, say, A$J? You need a name without expansion to assign to, but A$J=(...) doesn't work.
that's the letter "A" followed by the contents of the variable "J". If J == 2, then you could see it as A + ${J} == A2
@musiphil, printf -v "A$J" '%s' "value to assign" will do the trick. See BashFAQ #6.
@CharlesDuffy: Thanks for the pointer. printf -v works only for a single value, not for arrays, and read -a can split a string into arrays but is subject to the delimiter and the word separator, and cannot write to an associative array. declare seems to be the most versatile: declare "A$J=hello", declare -a "A$J=(hello world)", declare -A "A$J=([x]=hello [y]=world)".
@musiphil, ...back on 3.2.57, read appears to still do the trick: read -r 'arr[1]' <<<'hello'; declare -p arr behaves as-intended.
|
21

It’s worth to note, that even an index will be substituted at time the variable is evaluated:

$ A2=(building blocks)
$ Aref=A2[index]
$ index=1
$ echo "${!Aref}"
blocks

3 Comments

FANTASTIC. years of shell scripting + reading the manual at least a dozen times + explicit attempts at exactly this failed to surface this little gem! note, like literal indexes, any arithmetic expression is valid, including nested expansions, eg. cycle=(0 1 2); ref='cycle[i++%${#cycle[*]}]'; echo ${!ref} ${!ref} ${!ref} ${!ref} ${!ref} ${!ref} # => 0 1 2 0 1 2
I wonder if this is a feature explicitly intended to be so, or if we are exploiting side-effect of the indirect expansion, which could silently disappear from future releases.
@davide Still works! :-) I don't think this will change. Shell code is all about phases of macro-like literal expansion into words, and the man page has this to say: ... the first character of parameter is an exclamation point ... introduces a level of indirection ... this variable is expanded ... used in the rest of the substitution ... known as indirect expansion. AFAICT, it's 100% inline with expectations.
5

Today (with bash 4.3 and later), the best practice is to use nameref support:

A1=(apple trees)
A2=(building blocks)
A3=(color television)
J=2

declare -n A="A$J"
printf '%q\n' "${A[@]}"

...will properly emit:

building
blocks

This is also available as nameref A="A$J" on ksh93. See BashFAQ #6 for details.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.