我一直在阅读“Bash Pocket Reference”一书,并阅读了有关关联数组的部分。现在,尝试书中的示例代码给了我意想不到的答案:
istanev@inspiron5559:~$ data=([joe]=30 [mary]=25)
istanev@inspiron5559:~$ echo ${data[joe]}
25
istanev@inspiron5559:~$ echo ${data[mary]}
25
data[joe] 不是应该返回 30 吗?为什么它返回 25?我的 bash 版本是 4.3.46(1)-release。
答案1
默认情况下,bash 将数组处理为索引数组。
在这种情况下,索引被评估为算术表达式。
$ joe=3 mary=6
$ unset data
$ data=([joe]=111 [mary]=bbb)
$ declare -p data
declare -a data=([3]="111" [6]="bbb")
输出揭示了几件事:
- 该数组被索引:
-a
在输出中。 - 该数组确实包含两个值。
joe
值的索引与变量和的数值相匹配mary
。- 数组中包含的值可以是字符串。
即使索引被引用(甚至单引号)也是如此:
$ joe=3 mary=6
$ unset data
$ data=(["joe"]=111 ["mary"]=bbb)
$ declare -p data
declare -a data=([3]="111" [6]="bbb")
如果用作索引的字符串尚未定义为包含数字,则无论如何都会将其计算为具有零值的算术表达式。
$ unset joe ; unset mary ; unset data
$ data=([joe]=111 [mary]=bbb)
$ declare -p data
declare -a data=([0]="bbb")
发生的情况是,[joe]=111
计算结果为[0]=111
,将索引处的数组设置0
为111
。但随后,[mary]=bbb
计算结果为[0]=bbb
,从而将索引处的数组值替换0
为bbb
。
要真正拥有关联数组,必须在使用之前对其进行定义。
当它包含数据时不能更改。数组设置如上:
$ declare -A data
bash: declare: data: cannot convert indexed to associative array
但是清除它,我们可以:
$ joe=3 ; mary=6 ; unset data
$ declare -A data
$ data=([joe]=111 [mary]=bbb)
$ declare -p data
declare -A data=([joe]="111" [mary]="bbb" )
正如您所看到的,用作索引的字符串也是有效的变量名称并且它们包含值并不重要。它们用作关联数组的字符串索引。
答案2
关联数组需要使用typeset -A
或等效的东西(declare -A
或readonly -A
在 bash 中)显式声明。默认情况下,数组是一个“普通”数组,具有整数索引。整数索引数组上的非数字索引被解释为算术表达式,而算术表达式中未设置的变量名会默认解释为 0,因此data=([joe]=30 [mary]=25)
设置data[0]=30
、thendata[0]=25
和${data[whatever]}
is 元素 0,即25
。
bash-4.3$ indexed=([a]=aye [b]=bee [x+1]=cee)
bash-4.3$ echo length=${#indexed[@]} a=${indexed[a]} b=${indexed[b]} x+1=${indexed[x+1]} 1=${indexed[1]}
length=2 a=bee b=bee x+1=cee 1=cee
bash-4.3$ typeset -A associative=([a]=aye [b]=bee [x+1]=cee)
bash-4.3$ echo length=${#associative[@]} a=${associative[a]} b=${associative[b]} x+1=${associative[x+1]} 1=${associative[1]}
length=3 a=aye b=bee x+1=cee 1=
因为indexed
是一个具有数字索引的数组,indexed[a]
并且indexed[b]
都是indexed[0]
,并且indexed[x+1]
是indexed[1]
。对于关联数组,括号内的内容被解析为字符串(使用双引号中的通常扩展,因此您可以编写${associative[$key]}
)。