6.7 数组(Arrays)

Bash 提供两种单维数组,即索引数组和关联数组。任意变量都可作为索引数组使用;内建命令 declare 可显式声明一个数组。对于数组是没有最大长度的限制,也不会要求其成员具有连续的索引或赋值。索引数组使用从零开始的整数(也包括算术表达式,参见 6.5《Shell 算术运算》)进行引用;关联数组使用任意的字符串进行引用。除非有其它注释,索引数组的索引数字必须是非负的整数。

如果任意变量使用语法“name[subscript]=value”进行的赋值,则会自动创建一个索引数组。下标“subscript”会被视为一个算术表达式,其必须计算得出一个整数。可使用“delcare -a name”显式声明一个数组。使用“declare -a name[subscript]”也可进行数组声明,但下标“subscript”是忽略的。创建关联数组可使用“declare -A name”。

可使用内建命令 delcare 和 readonly 来指定数组变量的属性。每个属性都将应用于数组的所有成员。

可使用复合命令的形式“name=(value1 value2 … )”来对数组进行赋值,其中的每一个“value”都可以是这种形式“[subscript]=string”。索引数组的赋值除了“string”,其它的都不是必须出现的。当对索引数组赋值时,如果提供了可选的下标“subscript”,则是对该下标对应的数组成员进行赋值;否则,语句分配的元素索引是由已分配的最后一个索引加一。索引起始于零。

对列表中的每一个“value”都可以使用 shell 扩展(参见 3.5《Shell 扩展》)。

当对一个关联型数组赋值时,在复合赋值语句中的单词即可以是必须带有下标的赋值语句,或是翻译为一个“键-值”相互交替的单词序列列表:“name=(key1 value1 key2 value2 …)”。该形式可等同于“name=( [key1]=value1 [key2]=value2 …)”。在列表中的第一个单词决定了如何翻译后面的单词;在列表中的所有赋值都必须使用相同的方式。当使用“键/值”对时,不能缺失键或键为空;最后一个缺省的值会被认为是空字符串。

该语法同样也被内建命令 declare 所接受。可使用上面介绍的语法“name[subscript]=value”对数组中单个元素进行赋值。

当对一个索引数组进行赋值时,如果使用负数作为“name”的下标时,数字翻译为“name”最大索引相对多 1 的数。所以负索引是从数组末端向前计数,即“-1”索引代表最后一个元素。

可以使用“${name[subscript]}”来引用数组中的任意一个元素。大括号是为了防止与shell文件名扩展操作符冲突所必须出现的。如果下标“subscript”是“@”或“*”,则扩展为数组“name”的所有元素。这两个下标只有在单词出现在双引号中时才会有不同。如果单词出现在双引号里,“${name[*]}”则将数组中每一个成员,使用变量 IFS 的第一个字符进行连接,扩展为一个单词。而“${name[@]}”则将每一个数组元素扩展为分开的单词。当数组中没有元素时,“${name[@]}”则什么都不扩展。如果双引号扩展出现在一个单词内部,第一个参数的扩展与原单词开始部分连接,最后一个参数的扩展与原单词的末尾部分连接。这类似于特殊参数“@”和“*”的扩展。“${#name[subscript]}”扩展出“${name[subscript]}”的长度。如果下标“subscript”是“@”或“*”,则扩展出的结果是数组的元素个数。如果引用索引数组元素的下标的数值小于零,其会翻译为数组最大索引相对多 1 的数,所以负数索引是从数组末尾开始倒计数,“-1”引用到最后一个元素。

引用一个不带有下标的数组变量等同于引用了下标为 0 的数组变量。任意引用到带有有效下标的变量都是合法的,并且 bash 会按需求创建数组。

如果数组下标分配了一个值的话,则认为设置了数组变量。空字符串是有效的值。

同时获取一个数组的键(索引)和值是可行的。“${!name[@]}”和“${!name[*]}”扩展为分配给数组变量“name”的所有索引。当这两个扩展处于双引号之中时,其扩展方式与特殊参数“@”和“*”在双引号中的扩展相似。

内建命令 unset 用于摧毁数组。“unset name[subscript]”用于删除在索引位“subscript”的元素。对于索引数组的负数下标的使用在上面已经描述过了。释放最后一个数组变量的元素不会释放这个变量。当使用了“unset name”,其“name”是一个数组时,才会删除整个数组。删除带有“@”或“*”下标的数组时,也会删除整个数组。

当使用一个带有下标的数组名作为一个命令的参数时,例如 unset 命令,不会使用上面描述的单词扩展语法,而参数会进行 shell 的文件名扩展。如果不想进行文件名扩展,则必须将参数引起来。

内建命令“declare”、“local”和“readonly”都有“-a”选项用来指定一个索引数组,并且“-A”选项用来指定一个关联数组。如果两个选项都出现,“-A”具有高优先级。内建命令“read”可使用“-a”选项从标准输入读取一列单词并分配给一个数组。并且也可从标准输入读取多个值放入单独的数组元素中。内建命令“set”和“declare”有一种显示数组值的方式可重用于输入。