在 Shell 脚本(特别是 Bash)中,数组是一种非常实用的数据结构,用于批量处理文件名、参数列表或远程服务器地址。掌握它的定义、访问和遍历,可以让你的脚本更简洁、更强大。
1. 数组定义
Bash 支持普通索引数组(数字下标)和关联数组(字符串下标,需 Bash 4+)。定义数组时,用一对圆括号将元素括起来,元素之间用空格分隔。
# 定义普通索引数组(下标从 0 开始)
servers=(web01 web02 db01 cache01)
# 也可以用逐步赋值的方式
files[0]="/etc/hosts"
files[1]="/etc/resolv.conf"
files[2]="/etc/fstab"
# 使用通配符或命令输出直接生成数组
log_files=(/var/log/*.log)
users=($(awk -F: '{print $1}' /etc/passwd))
关联数组需要先 declare -A 声明,用来建立键值对映射:
declare -A ports
ports=([http]=80 [https]=443 [ssh]=22)
# 或者逐个赋值
ports[ftp]=21
2. 访问数组元素
访问数组元素必须使用 ${} 语法,并且用下标引用。
| 操作 | 语法 | 示例结果(以上方 servers 为例) |
|------|------|----------------------------------|
| 获取单个元素 | ${数组名[下标]} | ${servers[0]} → web01 |
| 获取所有元素 | ${数组名[@]} 或 ${数组名[*]} | web01 web02 db01 cache01 |
| 获取元素个数 | ${#数组名[@]} | 4 |
| 获取所有索引(键) | ${!数组名[@]} | 0 1 2 3 |
| 获取某元素长度 | ${#数组名[下标]} | ${#servers[0]} → 5 |
| 截取部分元素 | ${数组名[@]:起始:数量} | ${servers[@]:1:2} → web02 db01 |
注意:
@和在双引号内有重要区别。"${servers[@]}"会将每个元素当作独立的单词,而"${servers[]}"会把所有元素合并成一个字符串(以 IFS 第一个字符连接)。循环处理时务必用"${数组[@]}"避免单词拆分问题。
关联数组的访问使用键名:
echo "${ports[http]}" # 输出 80
echo "${!ports[@]}" # 输出所有键:http https ssh ftp
3. 遍历数组
遍历数组常见有两种方式:按元素直接遍历,或按索引遍历。
(1)直接遍历元素(推荐)
for host in "${servers[@]}"; do
echo "正在检查 ${host} ..."
ping -c 2 "${host}"
done
这种方式下变量 host 依次取到每个数组元素的值,书写简单,且能正确处理元素中包含的空格。
(2)通过索引遍历
当你还需要用到下标,或者遍历关联数组时:
# 索引数组
for i in "${!servers[@]}"; do
echo "第 ${i} 台服务器: ${servers[$i]}"
done
# 关联数组
for service in "${!ports[@]}"; do
echo "${service} 监听端口: ${ports[$service]}"
done
典型实用场景:批量文件操作
config_files=(/etc/nginx/nginx.conf /etc/ssh/sshd_config /etc/hosts)
for conf in "${config_files[@]}"; do
if [[ -f "$conf" ]]; then
echo "备份 $conf 到 /backup"
cp "$conf" /backup/
fi
done
4. 常用技巧备忘
- 追加元素:
servers+=(new_host) - 删除元素:
unset servers[1](注意下标会不连续) - 切片赋值:
daily_backups=("${all_backups[@]:0:7}") - 从字符串生成数组:利用
IFS自定义分隔符
IFS=',' read -ra fields <<< "name,age,city"
echo "${fields[1]}" # age
数组让 Shell 脚本有能力处理列表数据,而无需临时写入文件。只要注意用双引号包裹 ${数组[@]},就能避免绝大多数由空格和换行引起的意外,让你的脚本既健壮又清晰。