Bash 提供两类参数:标量值和数组。 标量值可以是字符串或数字,代表单个“事物”。
大多数情况下,标量值就足以存放你的数据。 但有时,你需要存储一串标量值。 这时就要用到数组。
数组有两种:
本文前几节介绍数字索引数组(下文直接简称为“数组”)。 关于关联数组的细节放在最后。
Bash 数组只能存储标量值。 无法创建二维数组(数组的数组)。
你可以把一个用括号括起来的元素列表(可以为空)赋给变量,从而初始化数组。
myarray=("one" "two" "three")
和所有变量赋值一样,等号两边不能有空格。
注意元素之间没有逗号分隔,而是一个用空白分隔的字符串列表。
事实上,括号内的空白多少完全随意。 可以使用换行,想用多少就用多少,让代码更好读。
myarray=(
"first element"
"second element"
"this is the last element"
)
以这种方式把一组字符串赋给数组变量,第一个字符串会存到下标 0,第二个存到下标 1,第三个存到下标 2,以此类推。
也可以通过指定需要的下标来初始化“稀疏数组”。
raindrops=([3]="Pling" [5]="Plang" [7]="Plong")
再强调一次:数组下标从 0 开始。 这段代码会输出 "true":
myarray=("one" "two" "three")
[[ "${myarray[0]}" == "one" ]] && echo true || echo false
你可以用declare命令(在函数内则是local)来指明某个变量将存放数组。
declare -a myarray
更地道的做法是直接赋值一个(可能为空的)带括号的列表来初始化变量。
正如上面的提示所说,访问数组元素的语法是${myarray[$index]}。
下标写在方括号里。
花括号是必需的。
对数字索引数组来说,下标是一个算术表达式。 Bash 手册写道:
在算术表达式中,shell 变量也可以直接用名字引用,而不必使用参数展开语法。
这意味着做“简单”的变量展开时并不需要$。
可以像这样访问元素,读起来稍微轻松一些:
i=5
echo "${myarray[i]}"
# ..............^
如果给定的下标在数组中不存在,展开结果就是空字符串。
要给数组中的某个条目赋值,用这个语法:
myarray[10]="hello"
要往数组里_追加_元素,用+=这个拼接赋值运算符,并加上括号:
myarray+=("new element 1" "new element 2")
通常你需要对数组的_值_进行迭代。
用一个for循环(你在循环概念里见过):
myarray=("one" "two" "three")
for value in "${myarray[@]}"; do
echo "$value"
done
注意展开数组的特殊语法:"${myarray[@]}"
这种参数展开形式会把数组的值替换成一个个独立的词。
它大致相当于:
for value in "${myarray[0]}" "${myarray[1]}" ... "${myarray[n]}"
"${myarray[@]}"这种形式对稀疏数组同样有效,只会展开已定义的值。
这种特殊展开(下标为@)只在双引号内才具有特殊含义。
这里再介绍两个迭代数组时需要用到的特殊语法。 手册里的 Shell 参数展开 一节对它们有说明。
要获取数组的_长度_,用${#myarray[@]},在名字前面加一个#。
数组的“长度”指其中元素的个数。
要取出数组的_下标_列表,用"${!myarray[@]}",在名字前面加一个!,并把整个展开放在双引号里。
对数字索引数组,下标会按数字顺序展开。
有时你既需要下标,也需要值。 有几种做法。
当你知道下标构成连续序列时,可以用_算术_for循环。
for ((i = 0; i < ${#myarray[@]}; i++)); do
echo "$i -> ${myarray[i]}"
done
注意${#myarray[@]}这个语法,#字符在数组名前面。
由于数组下标从 0 开始,数组最后一个下标比数组长度小 1。
当数组是稀疏数组时,可以用这种形式:
for index in "${!raindrops[@]}"; do
echo "$index -> ${raindrops[index]}"
done
注意${!myarray[@]}这个语法,!字符在数组名前面。
如果你想查看数组的内容,可以用带-p选项的declare命令:
a=("the 'first' element"
second
third
'the "last" element')
declare -p a
它会以规范的 shell 引用形式输出内容。
declare -a a=([0]="the 'first' element" [1]="second" [2]="third" [3]="the \"last\" element")
再强调一次,关联数组用字符串而不是数字作为下标。 上面写的一切对关联数组同样适用,只是有些小差别。 一般来说,我们说的是“键”而不是“下标”:关联数组把键映射到值。
和数组不同,关联数组必须先声明。
给declare加上-A选项(在函数内则是local):
declare -A mymap
myfunc() {
local -A my_local_map
# ...
}
键值对写在括号内的列表里:
mymap=([first]=foo [second]=bar [third]=baz)
declare -p mymap
输出为:
declare -A mymap=([second]="bar" [first]="foo" [third]="baz")
注意,关联数组是无序的。
声明和初始化可以写在一行里。
declare -A mymap=([first]=foo [second]=bar [third]=baz)
在较新的 Bash 版本中,初始化可以用(key value key value ...)这种列表。
declare -A mymap=(first foo second bar third baz)
访问元素仍然使用前面的语法:${mymap[$key]}。
关联数组的键不会在算术上下文中求值。 必须使用通常的参数展开语法。
赋值用和前面一样的语法:
mymap[$key]="$value"
可以用for value in "${mymap[@]}"循环来迭代。
不过,你通常还想同时拿到键和值。
用上面见过的"${!mymap[@]}"语法。
for key in "${!mymap[@]}"; do
echo "$key -> ${mymap[$key]}"
done
输出为
second -> bar
first -> foo
third -> baz
再次强调,键是没有顺序的。