数组

数组 属于 Bash

{other: "%{count} 个练习"}

关于 数组

Bash 提供两类参数:标量值和数组。 标量值可以是字符串或数字,代表单个“事物”。

大多数情况下,标量值就足以存放你的数据。 但有时,你需要存储一串标量值。 这时就要用到数组。

数组有两种:

  • 数字索引数组把整数键映射到字符串值。 (其他语言可能称之为“列表”或“序列”。)
  • 关联数组把字符串键映射到字符串值。 (其他语言可能称之为“映射”或“字典”。)

本文前几节介绍数字索引数组(下文直接简称为“数组”)。 关于关联数组的细节放在最后。

Caution

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")
Note

再强调一次:数组下标从 0 开始。 这段代码会输出 "true":

myarray=("one" "two" "three")
[[ "${myarray[0]}" == "one" ]] && echo true || echo false

你可以用declare命令(在函数内则是local)来指明某个变量将存放数组。

declare -a myarray

更地道的做法是直接赋值一个(可能为空的)带括号的列表来初始化变量。

访问数组元素

正如上面的提示所说,访问数组元素的语法是${myarray[$index]}。 下标写在方括号里。 花括号是必需的。

Note

对数字索引数组来说,下标是一个算术表达式。 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[@]}",在名字前面加一个!,并把整个展开放在双引号里。 对数字索引数组,下标会按数字顺序展开。

迭代数组下标

有时你既需要下标,也需要值。 有几种做法。

  1. 当你知道下标构成连续序列时,可以用_算术_for循环。

    for ((i = 0; i < ${#myarray[@]}; i++)); do
        echo "$i -> ${myarray[i]}"
    done
    

    注意${#myarray[@]}这个语法,#字符在数组名前面。 由于数组下标从 0 开始,数组最后一个下标比数组长度小 1。

  2. 当数组是稀疏数组时,可以用这种形式:

    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")
Note

注意,关联数组是无序的。

声明和初始化可以写在一行里。

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

再次强调,键是没有顺序的。

通过 GitHub 编辑 该链接会在新窗口或标签页中打开