How do I populate a bash associative array with co

2020-02-10 09:32发布

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=()

标签: bash
3条回答
爷、活的狠高调
2楼-- · 2020-02-10 10:00

Here is a traditional while loop approach to populate an associative array from a command's output:

while IFS= read -r; do
   declare -A z+="( $REPLY )"
done < <(printf '[first]=foo [second]=bar\n[third]=baz\n')

# check output
$> echo "${z[first]}, ${z[second]}, ${z[third]}"
foo, bar, baz

# or declare -p
$> declare -p z
declare -A z='([third]="baz" [second]="bar" [first]="foo" )'

EDIT: Your original attempt will also work with proper quotes:

$> unset z

$> declare -A z="( $(echo '[first]=foo [second]=bar') )"

$> declare -p z
declare -A z='([second]="bar" [first]="foo" )'
查看更多
贼婆χ
3楼-- · 2020-02-10 10:03

Given that this works:

declare -A z=([first]=$(echo 'foo') [second]=$(echo 'bar'))

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:

eval "declare -A z=($(echo '[first]=foo [second]=bar'))"

What is a "usual reason" to avoid eval?

查看更多
Juvenile、少年°
4楼-- · 2020-02-10 10:06

I imagine this is somewhat brittle, but you can make the entire z=(...) assignment the result of a command substitution.

declare -A "$(echo z="($(echo '[first]=foo [second]=bar'))")"
查看更多
登录 后发表回答