Shell脚本中的数组 gaunthan Posted on Jul 12 2016 ? ShellScript ? ## 概述 数组是一种包含值的集合的特殊变量,可以通过键(索引)来访问它。除非另外指定,否则bash中的数组都是从0开始索引,所以数组的第一个元素是`${array[0]}`。shell可以创建稀疏数组。shell中的数组只能是一维的。 ## 数组的赋值 为数组赋值主要有三种方式: - 一次一个 - 一次全部 - 按索引赋值 如果数组是通过“一次一个”或者“一次全部”的方法声明的,那么shell自动检测是否有数组正在被声明。另外,语句`declare -a myarray`可以用来向shell声明该变量是被作为数组来使用的。 ### 一次一个 对数组赋值的最简单直接的方式是每次对一个元素进行赋值。与常规变量一样,赋值时不使用`$`符号,只在引用时才使用。变量名后面的索引号要使用方括号括起来。除了简洁与清晰,这种方式的另一个优势是可以定义稀疏数组。如下面的数组就没有定义第二项(下标为1): ```bash array[0]=0 array[2]=2 ``` 与常规变量一样,值可以包含空格,但需要用引号或反斜线将其引用起来: ```bash name[0]=John name[1]="Li Yi" name[2]=Wang\ Wu ``` ### 一次全部 更高效的给数组赋值的方式是在单个命令中列出所有的值。方法是在圆括号中列出所有用空格分开的值: ```bash $ cat array.sh #!/bin/bash # array.sh staff=( John "Li Yi" Wang\ Wu ) echo ${staff[0]} echo ${staff[1]} echo ${staff[2]} $ ./array.sh John Li Yi Wang Wu ``` 这种赋值方法的缺点是不能作用于稀疏数组以及必须预先知道要赋的值,并且能够将值硬编码到脚本中或者由脚本自行计算其值。 ### 按索引赋值 这种方法是“一次一个”的简易版本,或者也可以把它看成是“一次全部”的更显式的方法。这种方法在一对括号中对值一起进行赋值,但索引与值是相对应的。这种方法主要用于创建稀疏数组,但也能用于清晰地描述元素的索引位置,而不用冗长的“一次一个”方法,因为它每次都需要给出变量名: ```bash array=( [0]=zero [1]=one [2]=two [5]=five [11]=eleven ) ``` ### 从源中一次全部读取 这种方法是“一次全部”的特殊情况:括号中的内容可以由shell自身来提供,无论来自文件名扩展还是任何命令或函数的输出。读取的源可以是通配符扩展得到的一系列文件。输出中的每一项都会赋值给数组的一个元素: ```bash stat=( $(cat /proc/$$/stat) ) # or stat=( `cat /proc/$$/stat` ) ``` 如果要逐行读取文件,则将**IFS**(Internal Field Separator,内部字段分隔符)设置为换行符后再读取。 ### 从输入读取 使用`-a`标志调用bash shell的内置命令`read`可以将元素读取到数组中,无论是从文件或是用户输入: ```bash $ read -a array 1 2 3 $ printf "%s\n" ${array[@]} 1 2 3 $ read array #没有加-a选项并且省略的索引值,所以默认是将输入写入到数组的第一个元素 -1 $ printf "%s\n" ${array[@]} -1 2 3 ``` ## 数组的访问 访问数组时必须使用花括号。如果省略索引,则假设访问第一个元素: ```bash $ array=( 1 2 3 ) $ echo ${array} # 省略索引,默认访问第一个 1 $ echo $array # 这种情况也可以访问第一个元素,但不是那么好 1 $ echo $array[1] # 避免这种错误的用法 1[1] $ echo ${array[1]} # 标准用法 2 ``` ### 用索引访问 数组的索引从0开始,第一个元素的下标为0。 ```bash $ greeting=( Hello. "Nice to meet you." ) $ echo ${greeting[0]} Hello. $ echo ${greeting[1]} Nice to meet you. ``` ### 数组的长度 计算数组元素的数量与计算常规变量的长度非常相似: ```bash # 返回$myvar变量中字符串的长度 ${#myvar} # 返回数组中的元素个数 ${#myarray[@]} # 或 ${#myarray[*]} ``` 对于稀疏数组,返回的还只是数组中实际赋过值的元素数目,这与数组使用的最大索引不相同。另外,如果要获取数组中某个元素的长度,应该使用下列语法: ```bash `${#array[index]}` # index为元素下标 ``` 例子如下: ```bash $ staff=( John "Li Yi" Wang\ Wu ) $ echo ${#staff[@]} # 数组的长度为3 3 $ echo ${#staff[0]} # "John"的长度为4 4 ``` ### 用变量索引访问 索引不必是硬编码出来的整数,还可以是其他变量值。可以使用变量来遍历一个**非稀疏**数组: ``` $ cat array1.sh #!/bin/bash # array1.sh staff=( John "Li Yi" Wang\ Wu ) for index in $(seq 0 $((${#staff[@]} - 1))) # 从0,1···到数组长度-1(最大下标) do echo "No.${index} ${staff[$index]}" done $ bash array1.sh No.0 John No.1 Li Yi No.2 Wang Wu ``` ### 从数组中选择元素 从数组中选择单个元素很简单,但有时需要从其中检索一定范围内的元素。可以使用语法`${array[@]:offset:number}`指定: ```bash $ staff=( John "Li Yi" Wang\ Wu ) $ echo ${staff[@]:0:2} # 从下标0开始,选择2个元素 John Li Yi $ echo ${staff[@]:0} # 从下标0开始,选择剩余的所有元素 John Li Yi Wang Wu ``` ### 显示整个数组 可以简单的通过`echo ${array[@]}`命令来实现,但更灵活的是使用`printf`添加文本并格式化输出: ```bash $ staff=( John "Li Yi" Wang\ Wu ) $ echo ${staff[@]} John Li Yi Wang Wu $ printf "Name : %s\n" "${staff[@]}" # 为${staff[@]}添加引号以避免名字的空格导致非预期的输出 Name : John Name : Li Yi Name : Wang Wu ``` 注意如果数组元素的值包含空格,则需要用引号括起来。 ### 反向引用 一般来说,我们都是通过索引来获取相应元素的值,但由于Shell允许稀疏数组,因此有时候我们可能不知道有哪些索引是有效的,这时候可以通过**反向引用**来查看数组中都有哪些有效的索引。反向引用的语法很简单,像显示整个数组那样,区别是数组名前多了一个感叹号'!',如下例子所示:  ## 关联数组 关联数组是bash4.0新增的一个特性。关联数组将值与索引连接(关联)到一起,所以可以将元数据与实际数据关联起来。关联数组必须以`declare -A`命令来进行声明: ```bash $ cat array2.sh #!/bin/bash # array2.sh declare -A staff staff=( [teacher]=John [brother]="Li Yi" [friend]=Wang\ Wu ) for relationship in teacher brother friend do echo "My ${relationship} is ${staff[${relationship}]}" done $ bash array2.sh My teacher is John My brother is Li Yi My friend is Wang Wu ``` 关联数组更有用的功能是对索引的名称进行**反向引用**。这意味着通过实际数据可以获得对应的索引名称。反向引用的语法是`${!array[@]}`: ```bash $ cat array3.sh #!/bin/bash # array3.sh declare -A staff staff=( [teacher]=John [brother]="Li Yi" [friend]=Wang\ Wu ) for relationship in ${!staff[@]} do echo "My ${relationship} is ${staff[${relationship}]}" done $ bash array3.sh My friend is Wang Wu My teacher is John My brother is Li Yi ``` ## 数组操作 ### 数组的复制 可以用`${array[@]}`的值定义一个新的数组来对原来的数组进行复制。这样会按照相同的方式保留空白字符: ```bash $ staff=( John "Li Yi" Wang\ Wu ) $ copy=( "${staff[@]}" ) # 加双引号使空白字符正确保留下来:copy=( "John" "Li Yi" "Wang Wu" ) $ printf "%s\n" "${copy[@]}" John Li Yi Wang Wu $ copy=( ${staff[@]} ) # 不加双引号使得空白字符没有被正确保留下来:copy=( John Li Yi Wang Wu ) $ printf "%s\n" "${copy[@]}" John Li Yi Wang Wu ``` 注意,这种方式并不适用于稀疏数组。因为在访问源数组的时候,获取到的是值序列,离散的下标信息已经丢弃了,因此目标数组不是稀疏的:  ### 向数组追加元素 向数组追加元素的方法与数组复制很相似,只需将数组复制语句进行扩展: ```bash $ staff=( John "Li Yi" Wang\ Wu ) $ staff=( "${staff[@]}" "Lin Feng" ) $ printf "%s\n" "${staff[@]}" John Li Yi Wang Wu Lin Feng ``` 另外,索引从0开始使得追加更加方便,如下所示: ```bash $ staff[${#staff[@]}]="Zhao zi" $ printf "%s\n" "${staff[@]}" John Li Yi Wang Wu Lin Feng Zhao zi ``` ### 从数组中删除元素 从数组中删除元素与删除变量相同,可以使用`array[index]=`或者`unset array[index]`。此外,可以通过`unset array`删除整个数组。但是通过`array=`只能清除数组第一个元素的值: ```bash $ staff=( John "Li Yi" Wang\ Wu ) # original $ printf "%s\n" "${staff[@]}" John Li Yi Wang Wu $ staff= # delete the first element of array "staff" $ printf "%s\n" "${staff[@]}" #empty string Li Yi Wang Wu $ unset staff[2] # delete the third element of array "staff" Li Yi $ unset staff # delete the array "staff" $ printf "%s\n" "${staff[@]}" $ echo ${#staff[@]} 0 ``` 赏 Wechat Pay Alipay Shell脚本中的函数和库 Shell脚本入门知识