5

This question bad substitution shell- trying to use variable as name of array does something similar to what I need but for arrays. I'm very new to bash scripting and what I need is to do something like this:

# input
humantocheck="human1"

declare -A human1
declare -A human2

human1=( ["records_file"]="xxxxx.txt")
human2=( ["records_file"]="yyyyy.txt")

echo ${$humantocheck[records_file]}

With expected output of:

xxxxx.txt

However I get a bad substitution error when I try this.

3
  • You would; humantocheck is not an array. If anything is going to work, it'll be a variation on ${${!humantocheck}[records_file]} but I'm not confident (I didn't find the magic combo). (See shell parameter expansion.) Commented Sep 2, 2016 at 17:08
  • @JonathanLeffler having a look at it. Still new to bash so it's all greek -) Commented Sep 2, 2016 at 17:31
  • Another section in the G[r]eek manual of relevance is arrays. One possibility is to create a generic associative array human from either human1 or human2 and then reference that. Unfortunately, I don't think there's a way to assign whole associative arrays in one swell foop. The best I've come up with uses a loop to copy the associative array element by element: for key in "${!human1[@]}"; do human[$key]="${human1[$key]}"; done. Generally speaking, using indirect variable names leads to complications, and a rethink/rewrite. Commented Sep 2, 2016 at 17:36

2 Answers 2

9

This is exactly the scenario that the bash 4.3 feature namevars (borrowed from ksh93) is intended to address. Namevars allow assignment, as opposed to lookup alone, and are thus more flexible than the ${!foo} syntax.

# input
humantocheck="human1"

declare -A human1=( ["records_file"]="xxxxx.txt" )
declare -A human2=( ["records_file"]="yyyyy.txt" )

declare -n human=$humantocheck # make human an alias for, in this case, human1
echo "${human[records_file]}"  # use that alias
unset -n human                 # discard that alias

See BashFAQ #6 for a comprehensive discussion of both associative arrays and indirect expansion in general.

Sign up to request clarification or add additional context in comments.

1 Comment

from where I'm standing this should be the accepted answer.
3

One way to do this with an indirect reference is:

ref=$humantocheck[records_file]
echo ${!ref}

Bash: Indirect Expansion Exploration is an excellent reference for indirect access to variables in Bash.

Note that the echo command, which was intended to be a minimal modification to the original code, is unsafe in several ways. A safe alternative is:

printf '%s\n' "${!ref}"

See Why is printf better than echo?.

5 Comments

It works for me. It's intriguing — I'm trying to fathom what it means about the implementation. Well done! I'd probably use ref="$humantocheck[records_file]" with double quotes to prevent mischief. The key can also be in a variable, of course: ref="$humantocheck[$key]".
Brilliant! Thanks! I have only a handful of keys in the array so I'm able to create variables like human_file=$humantocheck[human_name] at the top of my file and use ${!human_file} as a sort of template variable for all other commands throughout the file! Really cool.
Word splitting is not performed on expansions in assignments so ref=$humantocheck[$key] is no less safe than ref="$humantocheck[$key]".
@pjh question, using this syntax how would one check for the existence of a key? Atm deps_file=$app_type[deps_file] and if [ -n ${!deps_file+1} ]; then echo Not set...; else echo key is set...; fi isn't working for me
@Jonathan, the code has missing quotes, and the echos are the wrong way round. Try if [ -n "${!deps_file+1}" ]; then echo 'Key is set' ; else echo 'Key is not set' ; fi. It's better to use [[...]] instead of [...] in Bash code because, among other advantages, you mostly don't have to worry about quotes.

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.