2

I have three associative arrays:

declare -A start_obj end_obj gopath

start_obj['one']="start-obj-one"
start_obj['two']="start-obj-two"

end_obj['one']="end-obj-one"
end_obj['two']="end-obj-two"

gopath['start']="/path/to/start"
gopath['end']="/path/to/end"

I want to get the key and value of start_obj, end_obj arrays by the key of the gopath array, code show as below:

for t in "${!gopath[@]}"
do
    current=$t"_obj"[@]
    cd ${gopath[$t]}
    for k in ${!current}
    do    
        printf  "[$t]key is : $k ; value is : ${current[$k]}\n"                                                           
    done
done

But , the result of this code execution is :

[start]key is : start-obj-one ; value is : start_obj[@]
[start]key is : start-obj-two ; value is : start_obj[@]
[end]key is : end-obj-one ; value is : end_obj[@]
[end]key is : end-obj-two ; value is : end_obj[@]

The result I expect is:

[start]key is : one ; value is : start-obj-one
[start]key is : two ; value is : start-obj-two
[end]key is : one ; value is : end-obj-one
[end]key is : two ; value is : end-obj-two

So,how should I modify my code to get the results I expected?

2 Answers 2

2

With bash 4.3 or newer, you could use nameref variables:

for t in "${!gopath[@]}"; do
  (
    typeset -n current="${t}_obj"
    cd -P -- "${gopath[$t]}" || exit
    for k in "${!current[@]}"
    do    
      printf '%s\n' "[$t]key is: $k; value is: ${current[$k]}"
    done
  )
done

With older versions, you'd need to use eval:

for t in "${!gopath[@]}"; do
  (
    cd -P -- "${gopath[$t]}" || exit
    eval '
      for k in "${!'"$t"'_obj[@]}"
      do    
        printf "%s\n" "[$t]key is: $k; value is: ${'"$t"'_obj[$k]}"
      done
    '
  )
done

bash has a variable indirection operator: ${!varname}, unrelated to the ${!hash[@]} operator (and actually closer to the reverse of what ${!varname} is in ksh93), but it cannot be combined with the ${!hash[@]} operator (varname=hash; for key in "${!!varname[@]}"... won't work). For a shell with a usable variable indirection operator here (and that has supported associative arrays for much longer), you can look at zsh (using ${(P)varname}) which also lets you loop over both key and value at the same time:

typeset -A start_obj end_obj gopath
start_obj=(
  one  start-obj-one
  two  start-obj-two
)
end_obj=(
  one  end-obj-one
  two  end-obj-two
)
gopath=(
  start  /path/to/start
  end    /path/to/end
)
for t dir ("${(kv@)gopath}") (
  cd -P -- "$dir" || exit
  current=${t}_obj
  for key value ("${(kvP@)current}")
    printf '%s\n' "[$t]key is: $key; value is: $value}"
)

In any case, in both bash and zsh (and ksh93 the first shell that introduced associative arrays and that bash tried to copy), associative arrays are implemented as hash tables, so the elements are not stored in any particular order, so those code above will loop over them in a seemingly random order.

4
  • Thanks , my bash version is 4.2.46(2)-release ,I got an error: "typeset: -n: invalid option" .It seems that it does not support "typeset -n".
    – Shaun
    Mar 27, 2019 at 10:15
  • @shaun, see edit. Mar 27, 2019 at 10:36
  • The code you append is not work., i got the resule : "[start]key is : one ; value is : " , value is null . But, i know your thoughts. Thanks.
    – Shaun
    Mar 27, 2019 at 11:02
  • @shaun, yes sorry, I had forgotten to replace the second occurrence of current. See edit. Mar 27, 2019 at 11:05
0

With earlier bash versions, you can do this with indexed arrays, but not with associative arrays, using variable indirection:

$ declare -p start_obj end_obj gopath
declare -A start_obj='([one]="start-obj-one" [two]="start-obj-two" )'
declare -A end_obj='([one]="end-obj-one" [two]="end-obj-two" )'
declare -A gopath='([start]="/path/to/start" [end]="/path/to/end" )'

$ for t in "${!gopath[@]}"; do tmp="${t}_obj[@]"; ary=( "${!tmp}" ); declare -p ary; done
declare -a ary='([0]="start-obj-one" [1]="start-obj-two")'
declare -a ary='([0]="end-obj-one" [1]="end-obj-two")'

Note how we can get the values but not the keys of the associative arrays.

For more info, see the 4th paragraph in https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .