10.2. 数组变量

10.2.1. 创建数组

数组是一种包含多个值的变量。任何变量都可以用作数组。数组的大小没有最大限制,成员变量也不要求连续索引或赋值。数组是基于零的:第一个元素的索引号为 0。

间接声明通过以下语法完成,以声明一个变量

数组[索引号]=值

索引号 被视为一个算术表达式,其求值结果必须为正数。

显式声明数组使用 declare 内建命令完成

declare-a 数组名

带有索引号的声明也会被接受,但索引号将被忽略。可以使用 declarereadonly 内建命令指定数组的属性。属性应用于数组中的所有变量;你不能拥有混合数组。

数组变量也可以使用复合赋值以这种格式创建

数组=(值1 值2 ... 值N)

每个值都采用 [索引号=]字符串 的形式。索引号是可选的。如果提供了索引号,则将该索引分配给它;否则,分配的元素的索引是最后分配的索引号加一。 declare 也接受这种格式。如果未提供索引号,则索引从零开始。

在数组中添加缺失或额外的成员可以使用以下语法

数组名[索引号]=值

请记住,read 内建命令提供了-a选项,该选项允许读取和分配数组的成员变量的值。

10.2.2. 解引用数组中的变量

为了引用数组中项目的内容,请使用花括号。这是必要的,正如你可以从以下示例中看到的那样,为了绕过 shell 对扩展运算符的解释。如果索引号为 @*,则引用数组的所有成员。

[bob in ~] ARRAY=(one two three)

[bob in ~] echo ${ARRAY[*]}
one two three

[bob in ~] echo $ARRAY[*]
one[*]

[bob in ~] echo ${ARRAY[2]}
three

[bob in ~] ARRAY[3]=four

[bob in ~] echo ${ARRAY[*]}
one two three four

引用数组的成员变量的内容而不提供索引号,与引用第一个元素的内容相同,即索引号为零的元素。

10.2.3. 删除数组变量

unset 内建命令用于销毁数组或数组的成员变量

[bob in ~] unset ARRAY[1]

[bob in ~] echo ${ARRAY[*]}
one three four

[bob in ~] unset ARRAY

[bob in ~] echo ${ARRAY[*]}
<--no output-->

10.2.4. 数组示例

很难找到数组的实际使用示例。你会发现很多脚本实际上并没有在你的系统上做任何事情,但它们确实使用数组来计算数学序列,例如。这将是更有趣的例子之一……大多数脚本只是以过于简化和理论化的方式展示你可以用数组做什么。

这种枯燥的原因是数组是相当复杂的结构。你会发现大多数可以使用数组的实际例子已经在你的系统中使用数组实现了,但是是在更低的级别,在大多数 UNIX 命令都是用 C 编程语言编写的。一个很好的例子是 Bash history 内建命令。有兴趣的读者可以查看built-insBash 源代码树中的目录,并查看fc.def,它在编译内建命令时被处理。

好的例子很难找到的另一个原因是并非所有 shell 都支持数组,因此它们会破坏兼容性。

经过漫长的搜索,我终于找到了这个在互联网提供商处运行的例子。它将 Apache Web 服务器配置文件分发到 Web 服务器集群中的主机上

#!/bin/bash

if [ $(whoami) != 'root' ]; then
        echo "Must be root to run $0"
        exit 1;
fi
if [ -z $1 ]; then
        echo "Usage: $0 </path/to/httpd.conf>"
        exit 1
fi

httpd_conf_new=$1
httpd_conf_path="/usr/local/apache/conf"
login=htuser

farm_hosts=(web03 web04 web05 web06 web07)

for i in ${farm_hosts[@]}; do
        su $login -c "scp $httpd_conf_new ${i}:${httpd_conf_path}"
        su $login -c "ssh $i sudo /usr/local/apache/bin/apachectl graceful"

done
exit 0

首先执行两个测试,以检查是否正确的用户正在使用正确的参数运行脚本。需要配置的主机名在数组中列出farm_hosts。然后,所有这些主机都提供 Apache 配置文件,之后守护进程将重新启动。请注意使用了 Secure Shell 套件中的命令,加密了与远程主机的连接。

感谢 Eugene 和同事的贡献。

Dan Richter 贡献了以下示例。这是他面临的问题

"...在我的公司,我们在我们的网站上有演示,每周都必须有人测试所有演示。所以我有一个 cron 任务,它用可能的候选人填充一个数组,使用 date +%W 找到一年中的第几周,并执行模运算以找到正确的索引。幸运的人会收到电子邮件通知。"

这就是他解决问题的方法

#!/bin/bash
# This is get-tester-address.sh 
#
# First, we test whether bash supports arrays.
# (Support for arrays was only added recently.)
#
whotest[0]='test' || (echo 'Failure: arrays not supported in this version of
bash.' && exit 2)
                                                                                
#
# Our list of candidates. (Feel free to add or
# remove candidates.)
#
wholist=(
     'Bob Smith <bob@example.com>'
     'Jane L. Williams <jane@example.com>'
     'Eric S. Raymond <esr@example.com>'
     'Larry Wall <wall@example.com>'
     'Linus Torvalds <linus@example.com>'
   )
#
# Count the number of possible testers.
# (Loop until we find an empty string.)
#
count=0
while [ "x${wholist[count]}" != "x" ]
do
   count=$(( $count + 1 ))
done
                                                                                
#
# Now we calculate whose turn it is.
#
week=`date '+%W'`    	# The week of the year (0..53).
week=${week#0}       	# Remove possible leading zero.
                                                                                
let "index = $week % $count"   # week modulo count = the lucky person

email=${wholist[index]}     # Get the lucky person's e-mail address.
                                                                                
echo $email     	# Output the person's e-mail address.

此脚本随后在其他脚本中使用,例如这个脚本,它使用 here 文档

email=`get-tester-address.sh`   # Find who to e-mail.
hostname=`hostname`    		# This machine's name.
                                                                                
#
# Send e-mail to the right person.
#
mail $email -s '[Demo Testing]' <<EOF
The lucky tester this week is: $email
                                                                                
Reminder: the list of demos is here:
    http://web.example.com:8080/DemoSites
                                                                                
(This e-mail was generated by $0 on ${hostname}.)
EOF