「Shell」- 数组的使用

  CREATED BY JENKINSBOT

注意事项

并不是每种 Shell 都支持数组(比如 Bash 支持数组,但是 Dash 不支持数组),为了写出可移植脚本,不建议使用数组。

The Bourne shell or the Unix sh lanuage specification don’t support arrays.

定义 – 如何定义一个数组?

索引数组

# 声明索引数组
declare -a mIndexArray

# 索引数组也可以直接定义:
mIndexArray=(one two three)

关联数组

# 声明关联数组
declare -A mAssociativeArray

# 但是,关联数组不可以直接定义,必须先声明:
declare -A mAssociativeArray
mAssociativeArray=( [foo]=bar [baz]=quux [corge]=grault )

# 如果没有先声明为关联数组,则 mAssociativeArray=( [foo]=bar [baz]=quux [corge]=grault ) 默认为索引数组,并且其中的元素也发生了变化。

赋值 – 如何对数组进行赋值?

对索引数组和关联数组进行赋值方面,两者直接并无太大差异:

mIndexArray[5]=five
mAssociativeArray[five]=five

数组索引也可以是变量:

mAssociativeArray[$var]=value

即使索引是“存在空格的字符串”,也不需要引用:

# 下面三种是等价的
mAssociativeArray['key with space']=value
mAssociativeArray["key with space"]=value
mAssociativeArray[key with space]=value

也可以使用如下方式,以无需索引的方式向数组中添加元素:

# 如下示例向索引数组mIndexArray中添加了两个元素,但是无需指定索引
mIndexArray=()
mIndexArray+=('foo')
mIndexArray+=('bar')

取值 – 如何取值及打印整个数组?

从数组中取值

对索引数组和关联数组进行取值方面,两者直接并无太大差异:

	echo ${mIndexArray[5]}
	echo ${mAssociativeArray[five]}
	echo ${mArray[$var]}
	ehco ${mArray[var with space]}

打印整个数组

打印整个数组的方式有些特殊。下面的这个做法是错误的:

# 如下示例,索引数组只会打印第一个元素,而关联数组则输出空
echo $mIndexArray
echo $mAssociativeArray

打印数组的全部元素的正确形式应该是:

# 如下的两种形式并无差异,但打印的都是数组中值,并不包含索引

echo ${mIndexArray[*]}
echo ${mAssociativeArray[*]}

echo ${mIndexArray[@]}
echo ${mAssociativeArray[@]}

如果要打印索引,则应该:

# 如下示例将打印数组的所有索引

echo ${!mIndexArray[*]}
echo ${!mAssociativeArray[*]}

echo ${!mIndexArray[@]}
echo ${!mAssociativeArray[@]}

使用循环打印整个数组

上面的示例已经能够获取数组的索引和值了,所以循环也不会很麻烦:

如下示例分别演示了打印数组索引和值的方法:

for key in "${!mAssociativeArray[@]}"
do
	echo $key
	echo ${mAssociativeArray[$key]}
done

for value in "${mAssociativeArray[@]}"
do
	echo $value
done

for value in "${mIndexArray[@]}"
do
	echo $value
done

如何判断某个索引是否存在?

使用常用的 if 语句即可。如下,如果找到了索引,则打印“Found”字符串:

if [ ${mAssociativeArray[$var]} ]
	then echo "Found"
else
	echo "Not found"
fi

删除 – 如何删除数组及其中某个元素?

删除数组

如果要删除一个关联数组或者索引数组,可以进行unset操作:

unset mAssociativeArray
unset mIndexArray

注意事项:重新声明(declare)并不能删除覆盖原有的数组;指定要删除的数组时,没有美元($)符号;

删除元素

删除元素只需要指定数组中元素即可:

unset mAssociativeArray[key]
unset mIndexArray[0]

注意事项:指定要删除的数组元素时,没有美元($)符号;索引数组中元素的索引也不会前移(例如,删除索引为3的元素,那索引为4的元素的索引不会变为3)。

删除元素时,如果索引中包含空格

如果删除元素的索引包含空格则需要进行引用,这里比较特殊:

unset mAssociativeArray["key with space"]
unset mAssociativeArray["$var"]

这里比较特殊的。与取值、赋值时“即使存在空格也无需引用”的情形有些不同。

长度 – 如何获取数组的长度?

使用井号(#)来获取数组的长度,形式如下:

echo ${#mIndexArray[*]}
echo ${#mAssociativeArray[*]}

echo ${#mIndexArray[@]}
echo ${#mAssociativeArray[@]}

这种用法也可以用于计算字符串的长度。

传参 – 如何将数组作为函数参数传递?

这一点就比较奇怪了,如果要讲数组传递到函数里,有两个办法:
1)定义一个全局的数组;
2)或者,将数组的值全部传递到函数中,然后在函数组使用$@取到所有值,然后放在函数中的一个数组中。

目前,我还没有找到直接传递数组的方法。 参考「How to pass an array as function argument?」文章。

拼接 – 拼接数组元素

特殊场景:我们可以换个思路,使用 seq 命令,生成有规律的数据:

# seq -s ', ' -f '192.168.1.%.0f:2000' 10 15
192.168.1.10:2000, 192.168.1.11:2000, 192.168.1.12:2000, 192.168.1.13:2000, 192.168.1.14:2000, 192.168.1.15:2000

注意事项

奇怪的作用域

函数隐式创建的数组,将作用与全局:

unset mAssociativeArray
function createWithoutDeclare() {
	mAssociativeArray[foo]=bar;
}

echo ${mAssociativeArray[foo]}
createWithoutDeclare
echo ${mAssociativeArray[foo]}

函数显式创建的数组,将作用于本地:

unset mAssociativeArray
function createWithDeclare() {
	declare -A mAssociativeArray[foo]=bar;
}

echo ${mAssociativeArray[foo]}
createWithDeclare
echo ${mAssociativeArray[foo]}

这一特性是 declare 导致的,也不足为奇怪,可以参考declare的说明(执行help decalre命令来查看,因为declare是Bash的内建命令)。

在 Dash 中,使用数组

#!/bin/sh

var="this is a test|second test|the quick brown fox jumped over the lazy dog"
oldIFS=$IFS
IFS="|"
set -- $var
echo "$1"
echo "$2"
echo "$3"      # Note: if more than $9 you need curly braces e.g. "${10}"
IFS=$oldIFS

参考文献

Add a new element to an array without specifying the index in Bash
How to pass an array as function argument?
Bash associative array examples
Does `dash` support `bash` style arrays?
Looping over arrays, printing both index and value
Arrays in Unix Bourne Shell – Unix & Linux Stack Exchange