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

4

1 回答 1

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

让我们引用 bash 手册页:

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

但这仅适用于访问该值,并且不适用于其他 shell。

作为替代方案,您始终可以使用eval

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

手册页说:

   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.
于 2013-10-27T22:11:22.200 回答