The Bash man page describes use of ${!a} to return the contents of the variable whose name is the contents of a (a level of indirection).

I'd like to know how to return all elements in an array using this, i.e.,

a=(one two three)
echo ${a[*]}


one two three

I would like for:

echo ${!b[*]}

to return the same. Unfortunately, it doesn't, but returns 0 instead.


Given the replies, I now realise that my example was too simple, since of course, something like:


Will achieve exactly what I said I needed.

So, here's what I was trying to do:

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'


Of course, carefully reading the Bash man page shows that this won't work as expected because the last line simply returns the indices of the "array" $_LIST (not an array at all).

In any case, the following should do the job (as pointed out):

LIST=($(eval echo \${$_LIST[*]}))

or ... (the route that I went, eventually):

LIST_lys="lys1 lys2"

Assuming, of course, that elements don't contain whitespace.

  • Add [@] to the pointer _LIST="LIST_${whichone}[@]" and then, use LIST=("${!_LIST}") to copy the array. It is a good idea to use lower case variable names to avoid conflicts with environment variables (All caps).
    – user232326
    Mar 9, 2017 at 2:52

4 Answers 4


I think the use of indirect reference of bash variable should be treated literally.

Eg. For your original example:

a=(one two three)
echo ${a[*]} # one two three
echo ${!b[*]} # this would not work, because this notation 
              # gives the indices of the variable b which
              # is a string in this case and could be thought
              # as a array that conatins only one element, so
              # we get 0 which means the first element
echo ${!c} # this will do exactly what you want in the first
           # place

For the last real scenario, I believe the code below would do the work.

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

LIST=( "${!_LIST}" ) # Of course for indexed array only 
                     # and not a sparse one

It is better to use notation "${var[@]}" which avoid messing up with the $IFS and parameter expansion. Here is the final code.

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

LIST=( "${!_LIST}" ) # Of course for indexed array only 
                     # and not a sparse one
                     # It is essential to have ${!_LIST} quoted

You need to copy the elements explicitly. For an indexed array:


For an associative array (note that a is the name of the array variable, not a variable whose value is the name of an array variable):

typeset -A b
for k in "${!a[@]}"; do b[$k]=${a[$k]}; done

If you have the variable name in an array, you can use the element-by-element method with an extra step to retrieve the keys.

eval "keys=(\"\${!$name[@]}\")"
for k in "${keys[@]}"; do eval "b[\$k]=\${$name[\$k]}"; done

(Warning, the code in this post was typed directly in a browser and not tested.)

  • You're quite right, but unfortunately, that doesn't solve my problem (my example was too simplistic). I've provided an update to be more clear on my intention.
    – Eric Smith
    Sep 6, 2011 at 8:52
  • @Eric I think in ksh/bash you need eval at that stage. See my edit. Sep 6, 2011 at 10:21
  • +1, but as per your disclaimer, this actually doesn't quite work (the last bit of code), but only needs a few minor adjustments. Specifically, it should be \${!$name[@]} on the first line, so that the first expansion is only of '$name', and the ${!a[@]} is saved for the eval, and the same thing in the for loop, with \${$name}. The other two backslashes before '$k' aren't strictly necessary there either.
    – krb686
    Oct 30, 2016 at 23:45

${!b[*]} expands to the indices used in array b.

What you would like has to be done in two steps, so eval will help: eval echo \${$b[*]}. (Note the \ which ensures that the first $ will pass the first step, the variable expansion, and will be only expanded in the second step by eval.)

According to Parameter Expansion ! is both used for indirect expansion ({!a}), Names matching prefix (${!a*}) and List of array keys (${!a[*]}). Because List of array keys has the same syntax as your intended indirect expansion+array element expansion, the later is not supported as is.

  • 2
    ${!a} does expand to the value of the variable whose name is $a. This is rather tersely described in the manual, in the parragraph that begins with “If the first character of parameter is an exclamation point (!), a level of variable indirection is introduced.” Sep 6, 2011 at 7:35
  • Yep - @Gilles is right, but @manatwork, on second reading, I noticed that ${! is kinda ambigious since if it's an array you're dealing with, the behaviour is different.
    – Eric Smith
    Sep 6, 2011 at 8:50
  • @Gilles you are right on that sentence, but sadly it not applies as "The exceptions to this are the expansions of ${!prefix*} and ${!name[@]} described below." But my reply is certainly an ambiguous mess, so I will edit it.
    – manatwork
    Sep 6, 2011 at 9:02

To access arrays indirectly, just add [@] to the indirect variable b=a[@].

If this variables are set:

a=(one two three)
printf '<%s> ' "${a[@]}"; echo

Then, this will work:

printf '<%s> ' "${!b}"

Or simply:

echo "${!b}"

Such array could be copied as this:

newArr=( "${!b}" )

And then printed with:

declare -p newArr

In one script:

a=(one two three)
echo "original array"
printf '<%s> ' "${a[@]}"; echo

echo "Indirect array with variable b=a[@]"
printf '<%s> ' "${!b}"; echo

echo "New array copied in newArr, printed with declare"
newArr=( "${!b}" )
declare -p newArr

Of course, all the above will copy a non-sparse array. One in which all indexes have a value.

sparse arrays

An sparse array is one which may have non-defined elements.
For example a[8]=1234 defines one element, and, in bash, 0 to 7 do not exist.

To copy such sparse array use this method

  1. Print the old array:

    $ oldarr[8]=1234
    $ declare -p oldarr
    declare -a oldarr=([8]="1234")
  2. Replace the name of the array and capture the string:

    $ str=$(declare -p oldarr | sed 's/oldarr=/newarr=/')
  3. Eval the string so created, the new array has been defined:

    $ eval "$str"
    $ declare -p newarr
    declare -a newarr=([8]="1234")

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.