2

is there a way to access an array name dynamically?

the following loop works:

#!/bin/bash

for i in 1 2 3 4 5; do
    for j in 1 2 3 4 5; do
        state="i=$i, j=$j"
        case "$i" in
            1) p_1+=("$state");;
            2) p_2+=("$state");;
            3) p_3+=("$state");;
            4) p_4+=("$state");;
            5) p_5+=("$state");;
            *) break;;
        esac
    done
done
for i in {0..5}; do echo "${p_1[$i]}"; done
for i in {0..5}; do echo "${p_2[$i]}"; done
for i in {0..5}; do echo "${p_3[$i]}"; done
for i in {0..5}; do echo "${p_4[$i]}"; done
for i in {0..5}; do echo "${p_5[$i]}"; done

The output looks like:

i=1, j=1
i=1, j=2
i=1, j=3
i=1, j=4
i=1, j=5

i=2, j=1
i=2, j=2
i=2, j=3
i=2, j=4
i=2, j=5

i=3, j=1
i=3, j=2
i=3, j=3
i=3, j=4
i=3, j=5

i=4, j=1
i=4, j=2
i=4, j=3
i=4, j=4
i=4, j=5

i=5, j=1
i=5, j=2
i=5, j=3
i=5, j=4
i=5, j=5

But it has that ugly case statement in the middle and is not as flexible as it could be. I would like to be able to expand it without having to expand the case statement.

I tried this:

for i in 1 2 3 4 5; do
    for j in 1 2 3 4 5; do
        $(p_${i})+=("$i, j=$j")  # Does not work
        ${p_$i}+=("$i, j=$j")    # neither does this
    done
done

Is there some syntactic magic that would allow me to dynamically define and access array names? Any help is greatly appreciated.

I tried "michas" solution, as shown here:

#!/bin/bash
for i in 1 2 3 4 5; do
    for j in 1 2 3 4 5; do
        state=("i=$i, j=$j")
        eval "p_$i+=($state)"
        #also tried
        # IFS="_" state=("i=$i,j=$j") #failed to show j=
        # IFS="_" eval "p_$i=($state)" # failed to show j=
    done
done
for i in {0..5}; do
    for j in {0..5}; do 
        res=p_$i
        eval "echo \$p_$i cooked: ${!res}"
        #IFS="_" eval "echo \$p_$i cooked: ${!res}" #failed to show j=
    done
done

but even with the commented out regions, all returned the following(abridged) output :

i=1, cooked: i=1,
 :
i=1, cooked: i=1,
i=1, cooked: i=1,
 :
i=3, cooked: i=3,
i=3, cooked: i=3,
  :
i=4, cooked: i=4,
i=4, cooked: i=4,
   :
i=5, cooked: i=5,
i=5, cooked: i=5,

OK, solved my problem. This loop works as the first ( still limited but now limited to strings without a "+" ) but I can love with that.

#!/bin/bash
for i in 1 2 3 4 5; do
    for j in 1 2 3 4 5; do
        state=$(echo "i=$i, j=$j" | tr " " "+")
        eval "p_$i+=($state)"
    done
done


for i in {0..5}; do
    for j in {0..5}; do
        res=p_$i[$j]
        eval "echo ${!res}"| tr '+' ' '
    done
done

Thanks!.

1 Answer 1

2
p_5=foo
i=5
v=p_$i
echo ${!v} 
# => foo

Let's cite the bash man page:

   ${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 vari‐
   able; 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  expan‐
   sions  of ${!prefix*} and ${!name[@]} described below.  The exclamation
   point must immediately follow the left  brace  in  order  to  introduce
   indirection.

But this only works for accessing the value and is not portable for other shells.

As an alternative you can always use eval:

p_5=foo
i=5
eval "echo \$p_$i" # => foo
eval "p_$i=bar"
echo $p_5 # => bar

The man page says:

   eval [arg ...]
          The  args  are read and concatenated together into a single com‐
          mand.  This command is then read and executed by the shell,  and
          its  exit status is returned as the value of eval.  If there are
          no args, or only null arguments, eval returns 0.
Sign up to request clarification or add additional context in comments.

1 Comment

You are correct, I should have RTFM, however the solution did not work. There seems to be a problem with the IFS setting for inter array separator. I also tried adding changing the assignment to: "IFS="" state=("i=$i, j=$j") and also in the eval command: e.g. IFS="" eval "p_$i=($state)" and no matter what I try, I have not been able to get it to display the "j" setting. Using your approach, it seems to split the array elements on the " " boundary regardless. Thanks.

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.