On Mon, Dec 08, 2025 at 11:57:02 -0500, Chet Ramey wrote:
> When you parse the compound assignment to an associative array assignment,
> the shell has to determine whether or not you're using subscripted array
> assignment or key-value pair assignment (kvpair). The first word in the
> list determines how the assignment treats the list. This happens before
> the individual words are expanded, since it determines how you expand
> keys, subscripts, and values.
> 
> In this case, the word is
> 
> $(
>     echo [A]=a
>     echo '[B]=b'
>     echo ['C']=c
>   )
> 
> which is not a subscripted assignment, so it's treated as the key in a
> kvpair. There's no value, so the value is "".

This is unfortunate.

> If you single quote it, you'll just get a single element with the same key
> as above. If you double quote it, word expansion gives you
> 
> declare -A a=(\n  [A]=a\n[B]=b\n[C]=c\n)
> 
> and `declare' runs that through compound array assignment. Since the first
> word is a subscripted assignment, that's how the assignment gets treated,
> and you end up with three elements.

But it's parsing the expanded content as shell syntax:

hobbit:~$ kv=(one 1 'two point five' 2.5 $'\n\t \n' punc)
hobbit:~$ declare -A aa="( "${kv[@]}" )"
hobbit:~$ declare -p aa
declare -A aa=([punc]="" [two]="point" [one]="1" [five]="2.5" )

This is even worse.  There's a code injection vulnerability here:

hobbit:~$ kv=( x '$(date)' y 1 )
hobbit:~$ declare -A aa="( "${kv[@]}" )"
hobbit:~$ declare -p aa
declare -A aa=([y]="1" [x]="Mon Dec  8 14:31:58 EST 2025" )

As far as I know, the only safe way to serialize the keys and values of
an associative array into a string value, and then reassign that string
value back to its constituent associative array later, is to use the @K
expansion plus eval:

hobbit:~$ declare -A aa=( [one]=1 [two point five]=2.5 [$'\n\t \n']=punc )
hobbit:~$ saved="${aa[*]@K}"
hobbit:~$ printf '%s\n' "$saved"
$'\n\t \n' "punc" "two point five" "2.5" one "1" 
hobbit:~$ eval declare -A "aa=($saved)"
hobbit:~$ declare -p aa
declare -A aa=([$'\n\t \n']="punc" ["two point five"]="2.5" [one]="1" )

If there's a better way, I'd love to know about it.  Of course, one could
use a NUL delimiter between keys and elements in a file, but not in a
string value stored in a variable.

Reply via email to