I'm trying to populate an associative array with the output of a command. I can do it without a command as:
$ declare -A x=( [first]=foo [second]=bar )
$ echo "${x[first]}, ${x[second]}"
foo, bar
and I can populate a non-associative array with command output as:
$ declare y=( $(echo 'foo bar') )
$ echo "${y[0]}, ${y[1]}"
foo, bar
but when I try to build on both of the above to create a statement that will populate an associative array from a command, I get the following error message:
$ declare -A z=( $(echo '[first]=foo [second]=bar') )
-bash: z: $(echo '[first]=foo [second]=bar'): must use subscript when assigning associative array
Why am I getting that error message and what is the correct syntax to populate an associative array with the output of a command? I am trying to avoid using eval
for the usual reasons, do not want to use a temp file, and of course echo
is just being used as an example of a command that produces the effect in question, the real command will be more complicated.
So, based on a couple of the answers below, it looks like it was just my quoting that was a problem:
$ declare -A z="( $(echo '[first]=foo [second]=bar') )"
$ echo "${z[first]}, ${z[second]}"
foo, bar
and with spaces in the indices and values:
$ declare -A z="( $(echo '[first field]="foo with space" [second]="space bar"') )"
$ echo "${z[first field]}, ${z[second]}"
foo with space, space bar
EDIT in response to a question in the comments about why the quotes are necessary (How do I populate a bash associative array with command output?) - I don't exactly know but maybe someone else can explain using the results of this script as reference (not expecting the specified indices to be used in the indexed arrays, they're just part of the strings being populated as the array values):
$ cat tst.sh
#!/bin/env bash
set -x
printf 'Indexed, no quotes\n'
declare -a w=( $(echo '[first]=foo [second]=bar') )
declare -p w
printf '\n---\n'
printf 'Indexed, with quotes\n'
declare -a x="( $(echo '[first]=foo [second]=bar') )"
declare -p x
printf '\n---\n'
printf 'Associative, no quotes\n'
declare -A y="( $(echo '[first]=foo [second]=bar') )"
declare -p y
printf '\n---\n'
printf 'Associative, with quotes\n'
declare -A z=( $(echo '[first]=foo [second]=bar') )
declare -p z
.
$ ./tst.sh
+ printf 'Indexed, no quotes\n'
Indexed, no quotes
+ w=($(echo '[first]=foo [second]=bar'))
++ echo '[first]=foo [second]=bar'
+ declare -a w
+ declare -p w
declare -a w=([0]="[first]=foo" [1]="[second]=bar")
+ printf '\n---\n'
---
+ printf 'Indexed, with quotes\n'
Indexed, with quotes
++ echo '[first]=foo [second]=bar'
+ declare -a 'x=( [first]=foo [second]=bar )'
+ declare -p x
declare -a x=([0]="bar")
+ printf '\n---\n'
---
+ printf 'Associative, no quotes\n'
Associative, no quotes
++ echo '[first]=foo [second]=bar'
+ declare -A 'y=( [first]=foo [second]=bar )'
+ declare -p y
declare -A y=([second]="bar" [first]="foo" )
+ printf '\n---\n'
---
+ printf 'Associative, with quotes\n'
Associative, with quotes
+ z=($(echo '[first]=foo [second]=bar'))
./tst.sh: line 24: z: $(echo '[first]=foo [second]=bar'): must use subscript when assigning associative array
+ declare -A z
+ declare -p z
declare -A z=()
Here is a traditional while loop approach to populate an associative array from a command's output:
EDIT: Your original attempt will also work with proper quotes:
Given that this works:
I'm guessing that Bash needs to see the associative array initialization list before doing any substitutions. So I don't see a way to avoid
eval
:What is a "usual reason" to avoid
eval
?I imagine this is somewhat brittle, but you can make the entire
z=(...)
assignment the result of a command substitution.