Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating Bash associative arrays from the output of a function: Why does "declare -A foo=$(bar)" work but not "declare -A foo; foo=$(bar)"?

Tags:

bash

shell

Consider the following bash snippets. In them, I'm creating two associative arrays in different ways, and then printing one of the entries.

Scenario 1. Declaring and assigning in one statement. Works as expected:

make_person() { echo '([firstName]=Bob [lastName]=Black)'; }
declare -A person1=$(make_person)
echo "${person1[firstName]}"

Output:

Bob

Scenario 2. Declaring and assigning on two different lines. No output:

declare -A person2
person2=$(make_person)
echo "${person2[firstName]}"

Output:

Why does Scenario 1 succeed in printing the [firstName] entry while Scenario 2 does not? Are both scenarios defined and expected behaviour?

like image 435
jmrah Avatar asked Dec 22 '22 15:12

jmrah


2 Answers

Because declare re-interprets and parses the arguments to detect ( ) in variable assignment and then do array assignments. Whereas in normal assignment var=anything is always a normal assignment, in var=(anything) the ( ) are detected when parsing the expression (when bash reads the line/input to execute). declare works more like eval, it re-evaluates the input.

What is the bash specific reason that Scenario 1 succeeds in printing the [firstName] entry, and Scenario 2 does not?

The "bash specific reason" is I guess it was programmed that way. The reason would be also to be compatible with POSIX, so that var=$(anything) should result in a normal variable as in POSIX.

Are both scenarios defined

"Defined" is a bit too much, I do not see much in bash documentation about this specific syntax, but

and expected behaviour?

Yes, this is working as expected.

like image 119
KamilCuk Avatar answered Jan 13 '23 13:01

KamilCuk


Just to follow up on KamilCuk's excellent answer. This is really a comment but for the formatting.

To see the effect of declare -A person2; person2=$(make_person) use declare -p

$ declare -p person1 person2
declare -A person1=([lastName]="Black" [firstName]="Bob" )
declare -A person2=([0]="([firstName]=Bob [lastName]=Black)" )

To get this to "work", you need eval

$ eval "person2=$(make_person)"
$ declare -p person1 person2
declare -A person1=([lastName]="Black" [firstName]="Bob" )
declare -A person2=([lastName]="Black" [firstName]="Bob" )

But use the eval-like powers of declare as you discovered in the first form: declare -A ary=$(cmd)

like image 21
glenn jackman Avatar answered Jan 13 '23 11:01

glenn jackman