How to use a variable as part of an array name
Try doing this :
$ arrayA=(1 2 3)
$ x=A
$ var=array$x[@]
$ echo ${!var}
1 2 3
NOTE
- from
man bash
(parameter expansion) :
${parameter} The value of parameter is substituted. The braces are required when parameter is a positional parameter with more than one
digit, or when parameter is followed by a character which is not to be interpreted as part of its name.
* If the first character of parameter is an exclamation point (!), a level of variable indirection is introduced. Bash uses the value of the variable formed from the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of parameter itself. This is known as indirect expansion. * The exceptions to this are the expansions of ${!prefix*} and ${!name[@]} described below. The exclamation point must immediately follow the left brace in order to introduce indirection.
While you can use the indirect access as pointed in another answer, another way (in ksh and Bash 4.3 and newer) would be to use namerefs. Especially in the case of arrays this may be more useful since you can index the array through the nameref and don't need to put the index in the variable used as the reference.
arr1=(a b c)
arr2=(x y z)
typeset -n p=arr1 # or 'declare -n'
echo "${p[1]}" # prints 'b'
This doesn't work through the indirect access:
q=arr2
echo "${!q}" # prints 'x', the same as $arr2
echo "${!q[1]}" # doesn't work, it tries to take q[1] as a reference
As a C programmer might put it, ${!q[1]}
here acts as if q
was an array of pointers, instead of being a pointer to an array.
This took a lot of trial and error but eventually worked.
I took some inspiration from Youness. But all other answers did not help on my old bash (suse11sp1[3.2.51(1)-release])
The 'for' loop refused to expand the indirect array, instead you need to pre-expand it, use that to create another array with your new variable name. My example below shows a double loop, as that is my intended use.
THEBIGLOOP=(New_FOO New_BAR)
FOOthings=(1 2 3)
BARthings=(a b c)
for j in ${THEBIGLOOP[*]}
do
TheNewVariable=$(eval echo \${${j#New_}things[@]})
for i in $TheNewVariable
do
echo $j $i" hello"
echo
done
done
I'm using # to delete the "New_" from the first array entry, then concatenating with "things", to get "FOOthings". \${} with echo and eval, then do their thing in order without throwing errors, which is wrapped in a new $() and assigned the new variable name.
$ Test.sh
New_FOO 1 hello
New_FOO 2 hello
New_FOO 3 hello
New_BAR a hello
New_BAR b hello
New_BAR c hello
UPDATE ##### 2018/06/07
I've recently discovered one more spin on this issue. The variable created is not actually an array, but a space delimited string. For the task above this was ok, because of how "for" works, it doesn't read the array, it is expanded and then looped through, see extract below:
for VARIABLE in 1 2 3 4 5 .. N
do
command1
command2
commandN
done
But, I then needed to use it as an array. For this I needed to perform one more step. I took code verbatim by Dennis Williamson. I've tested it and it works fine.
IFS=', ' read -r -a TheNewVariable <<< ${TheNewVariable[@]}
The "IFS=', '" is a variable containing your deliminator. "read" with "-a" cuts and feeds the sting back into the array variable. Note, this has no respect for quotation marks, but there are a few options in read to manage this, e.g. I've removed the -r flag which I didn't need. So I have now combined this addition in the variable creation, which allows the data to be treated and addressed as it should.
THEBIGLOOP=(New_FOO New_BAR)
FOOthings=(1 2 3)
BARthings=(a b c)
for j in ${THEBIGLOOP[*]}
do
IFS=', ' read -a TheNewVariable <<< $(eval echo \${${j#New_}things[@]})
for i in ${TheNewVariable[@]} #Now have to wrap with {} and expand with @
do
echo $j $i" hello"
echo ${TheNewVariable[$i]} #This would not work in the original code
echo
done
done