bash中有两种数组:一种是索引数组(indexed array),也就是通过整数下标来索引数组,对应高级语言中的数组(array);另一种是关联数组(associative array),也就是通过任意字符串来查找元素,对应高级语言中的映射表(map或者hash map)。bash只支持一维数组,不过数组大小没有限制。注意索引数组的下标是从0开始的。另外,关联数组只在bash 4.0以上版本支持。
数组声明
其实不用声明,只需要按数组方式直接赋值给变量即可,不过为了代码清晰,可以显式声明一下。
# 全局变量 declare -a arr # 函数的局部变量 local -a arr
# 全局变量 declare -A arr # 函数的局部变量 local -A arr
数组赋值和引用
arr=(["a"]=var1 ["bc"]=var2 ["def"]=var3)
- 索引数组除了按照上面的方法来赋值(用数字做下标)之外,还可以省略下标值:
arr=(var0 var1 var2 var3)
arr["a"]=var1 arr["bc"]=var2 arr["def"]=var3
- 和下标赋值类似,引用数组采用形式${name[subscript]},这里加大括号是为了避免bash扩展变量歧义:
# 关联数组 echo ${arr[foo]} # 索引数组 echo ${arr[0]} # 不用下标等价于用下标{{0}}去访问元素(关联数组也一样): echo $arr
遍历整个数组
如果用用 @ 或者 * 作为下标去访问数组,那么bash认为是展开数组元素。两者的区别只在有双引号包围变量的情况下,这时用 @ 访问仍然列出所有的元素,而用 * 访问会把所有元素合并到一起,成为一个元素。所以,对于一般的遍历数组的任务来说,应该采用 @ 作为下标访问数组进行扩展。
- 获取数组大小(从结果上看 ${#arr[@]} 和 ${#arr[*]} 没有区别):
declare -A arr arr=([hello]=world [a b]="c d") # 输出2 echo "${#arr[@]}" # 输出2 echo "${#arr[*]}"
- 输出数组所有键值(注意 ${!arr[@]} 和 ${!arr[*]} 的差别:
declare -A arr arr=([hello]=world [a b]="c d") # 用 @ 扩展输出两行(注意是各项之间的位置变化了,按字母顺序): # a b # hello for k in "${!arr[@]}" do echo "$k" done # 用 * 扩展合并所有的项,只输出一行: # a b hello for k in "${!arr[*]}" do echo "$k" done
- 输出数组所有值(注意 ${arr[@]} 和 ${arr[*]} 的差别:
declare -A arr arr=([hello]=world [a b]="c d") # 用 @ 扩展输出两行: # c d # world for v in "${arr[@]}" do echo "$v" done # 用 * 扩展合并所有的项,只输出一行: # c d world for v in "${arr[*]}" do echo "$v" done
declare -A arr arr=([hello]=world [a b]="c d") # 输出: # a b -> c d # hello -> world for k in "${!arr[@]}" do echo "$k -> ${arr[${k}]}" done
数组销毁
# 销毁单个元素 unset arr[1] # 销毁整个数组 unset arr unset arr[@] unset arr[*]
注意事项
在使用关联数组时,需要注意赋值的时候给定的键值如果有空格,那是否加引号是有区别的!加了引号的字符串中间的空格前会加一个反斜线,不知道这是不是bash的一个bug。示例如下:
declare -A arr # 注意前者键值没有用引号包围,后者用引号包围了,从后续的结果看,这两者被当成不同的键值了 arr=([a b]="c d" ["a b"]="x y") # 输出: # a\ b -> x y # a b -> c d for k in "${!arr[@]}" do echo "$k -> ${arr[${k}]}" done