According to the recipe described in Associative arrays in zsh, I tried the following script to test a loop through an associative Array in Zsh 5.5.1:
typeset -A emap
emap=( X y U v )
for key val in ${(kv)emap}
do
echo key=$key val=$val
done
I get the expected output
key=U val=v
key=X val=y
But when I write the example like this:
env_code ()
{
typeset -A emap
emap=( MX1 Mme VOX Vbo MS1 Msc JRUBY_VERSION '()' FOO '')
local ename
local abbr
for ename abbr in ${(kv)emap}
do
echo ename=$ename abbr=$abbr
done
}
env_code # Invoking the function
I get the output
ename=FOO abbr=JRUBY_VERSION
ename=() abbr=MX1
ename=Mme abbr=MS1
ename=Msc abbr=VOX
ename=Vbo abbr=
We see that keys and values are exchanged. I would have expected the output
ename=MX1 abbr=Mme
ename=VOX abbr=Vbo
The array itself is intact:
% typeset -A emap
% emap=( MX1 Mme VOX Vbo MS1 Msc JRUBY_VERSION '()' FOO '')
% declare -p emap
typeset -A emap=( [FOO]='' [JRUBY_VERSION]='()' [MS1]=Msc [MX1]=Mme [VOX]=Vbo)
Note, in particular, the order of the key-value pairs.
However, when you expand ${(kv)emap}
without quotes, the empty string value of the key FOO
is lost.
% print -l ${(kv)emap}
FOO
JRUBY_VERSION
()
MX1
Mme
MS1
Msc
VOX
Vbo
As a result, FOO
is "mapped" to the value JRUBY_VERSION
, not ''
.
Quoting the expansion restores the expected behavior. With quotes, you need the explicit indexing operation to produce individual arguments to print
.
% print -l "${(kv)emap[@]}"
FOO
JRUBY_VERSION
()
MX1
Mme
MS1
Msc
VOX
Vbo
Without [@]
, the empty string is still preserved, just harder to notice.
% print -l "${(kv)emap}"
FOO JRUBY_VERSION () MX1 Mme MS1 Msc VOX Vbo
# Note the two spaces between FOO and JRUBY_VERSION.
Unquoted parameter expansions are not subject to word splitting, but they are subject to quote removal, and with no characters remaining after the quotes are removed from the empty string, there is no argument left after the expansion.