Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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[*]}

returns

one two three

I would like for:

b=a
echo ${!b[*]}

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

Update

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

b=("${a[@]}")

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'

_LIST=LIST_$whichone
LIST=${!_LIST[*]}

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"
...
LIST=(${!_LIST})

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

share|improve this question
add comment

3 Answers

up vote 2 down vote accepted

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
b=a
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
c='a[*]'
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_$whichone"[*]
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_$whichone"[@]
LIST=( "${!_LIST}" ) # Of course for indexed array only 
                     # and not a sparse one
                     # It is essential to have ${!_LIST} quoted
share|improve this answer
add comment

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

b=("${a[@]}")

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.)

share|improve this answer
    
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 '11 at 8:52
    
@Eric I think in ksh/bash you need eval at that stage. See my edit. –  Gilles Sep 6 '11 at 10:21
add comment

${!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.

share|improve this answer
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.” –  Gilles Sep 6 '11 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 '11 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 '11 at 9:02
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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