Bash数组操作:定义、遍历与常用技巧
Bash 数组基础:定义与初始化
定义索引数组
在 Bash 中,定义索引数组非常简单。索引数组的索引从 0 开始,就像许多其他编程语言一样。可以使用以下几种方式来定义和初始化一个索引数组。
- 逐个赋值:
my_array[0]="apple"
my_array[1]="banana"
my_array[2]="cherry"
在这个例子中,我们分别为数组 my_array
的 0、1、2 索引位置赋值。
- 一次性赋值:
my_array=("apple" "banana" "cherry")
这种方式更加简洁,直接在定义数组时就为其填充了元素。这里,数组的索引会自动从 0 开始顺序分配。
- 指定索引赋值:
my_array=( [3]="date" [5]="fig" )
在这个例子中,我们跳过了索引 0 - 2 和 4,直接为索引 3 和 5 赋值。未赋值的索引位置的值为空。
定义关联数组
关联数组(也称为哈希表或字典)在 Bash 中从版本 4.0 开始支持。与索引数组不同,关联数组使用任意的字符串作为索引。
- 启用关联数组支持:
在使用关联数组之前,需要先声明要使用关联数组类型,通过
declare -A
命令来实现。
declare -A my_assoc_array
- 逐个赋值:
my_assoc_array["fruit1"]="apple"
my_assoc_array["fruit2"]="banana"
这里我们为关联数组 my_assoc_array
分别设置了键为 fruit1
和 fruit2
的值。
- 一次性赋值:
declare -A my_assoc_array=( ["fruit1"]="apple" ["fruit2"]="banana" )
这种方式在声明关联数组的同时就进行了初始化。
访问数组元素
访问索引数组元素
要访问索引数组中的元素,使用数组名加上中括号括起来的索引值。例如:
my_array=("apple" "banana" "cherry")
echo ${my_array[0]} # 输出 apple
echo ${my_array[1]} # 输出 banana
如果要访问数组的所有元素,可以使用 *
或 @
通配符。
my_array=("apple" "banana" "cherry")
echo ${my_array[*]} # 输出 apple banana cherry
echo ${my_array[@]} # 输出 apple banana cherry
不过,当 *
或 @
被双引号包围时,情况有所不同。
my_array=("apple" "banana cherry")
echo "${my_array[*]}" # 输出 apple banana cherry
echo "${my_array[@]}" # 输出 apple banana cherry
这里,"${my_array[*]}"
将数组元素作为一个字符串输出,而"${my_array[@]}"
会将每个元素作为独立的字符串输出,这在处理包含空格的元素时非常重要。
访问关联数组元素
访问关联数组元素同样使用数组名加中括号,但中括号内是键值。例如:
declare -A my_assoc_array=( ["fruit1"]="apple" ["fruit2"]="banana" )
echo ${my_assoc_array["fruit1"]} # 输出 apple
数组遍历
遍历索引数组
- 使用
for
循环遍历索引:
my_array=("apple" "banana" "cherry")
for i in ${!my_array[*]}; do
echo "Index $i: ${my_array[$i]}"
done
在这个脚本中,${!my_array[*]}
返回数组 my_array
的所有索引。for
循环遍历这些索引,并输出每个索引及其对应的值。
- 使用
for
循环遍历元素:
my_array=("apple" "banana" "cherry")
for element in ${my_array[@]}; do
echo "Element: $element"
done
这里,for
循环直接遍历数组的元素并输出。
遍历关联数组
- 使用
for
循环遍历键值对:
declare -A my_assoc_array=( ["fruit1"]="apple" ["fruit2"]="banana" )
for key in ${!my_assoc_array[*]}; do
echo "Key: $key, Value: ${my_assoc_array[$key]}"
done
${!my_assoc_array[*]}
返回关联数组 my_assoc_array
的所有键。for
循环遍历这些键,并通过键获取对应的值输出。
数组常用操作技巧
获取数组长度
- 索引数组长度:
对于索引数组,可以使用
#
符号结合数组名和通配符来获取数组的长度。
my_array=("apple" "banana" "cherry")
length=${#my_array[*]}
echo "Array length: $length" # 输出 Array length: 3
- 关联数组长度: 获取关联数组长度的方法与索引数组类似。
declare -A my_assoc_array=( ["fruit1"]="apple" ["fruit2"]="banana" )
length=${#my_assoc_array[*]}
echo "Assoc Array length: $length" # 输出 Assoc Array length: 2
添加元素到数组
- 向索引数组添加元素:
要向索引数组末尾添加元素,可以使用
+=
操作符。
my_array=("apple" "banana")
my_array+=("cherry")
echo ${my_array[@]} # 输出 apple banana cherry
- 向关联数组添加元素: 向关联数组添加元素只需为新的键赋值即可。
declare -A my_assoc_array=( ["fruit1"]="apple" )
my_assoc_array["fruit2"]="banana"
删除数组元素
- 删除索引数组元素:
使用
unset
命令可以删除数组中的元素。
my_array=("apple" "banana" "cherry")
unset my_array[1]
echo ${my_array[@]} # 输出 apple cherry
这里删除了索引为 1 的元素。
- 删除关联数组元素:
同样使用
unset
命令删除关联数组中的元素,通过键来指定要删除的元素。
declare -A my_assoc_array=( ["fruit1"]="apple" ["fruit2"]="banana" )
unset my_assoc_array["fruit1"]
数组切片
在 Bash 中,可以获取数组的一部分,即数组切片。
my_array=("apple" "banana" "cherry" "date" "fig")
# 获取从索引 1 开始,长度为 3 的切片
slice=${my_array[@]:1:3}
echo $slice # 输出 banana cherry date
这里,${my_array[@]:1:3}
表示从索引 1 开始,取 3 个元素的切片。
数组排序
虽然 Bash 本身没有内置的数组排序函数,但可以借助外部工具如 sort
来实现。
- 索引数组排序:
my_array=("cherry" "apple" "banana")
sorted_array=($(echo ${my_array[@]} | tr ' ' '\n' | sort))
echo ${sorted_array[@]} # 输出 apple banana cherry
这里先将数组元素通过 tr
命令转换为每行一个元素,然后使用 sort
命令排序,最后重新构建数组。
- 关联数组排序(按键排序):
declare -A my_assoc_array=( ["fruit3"]="cherry" ["fruit1"]="apple" ["fruit2"]="banana" )
keys=(${!my_assoc_array[*]})
sorted_keys=($(echo ${keys[@]} | tr ' ' '\n' | sort))
for key in ${sorted_keys[@]}; do
echo "Key: $key, Value: ${my_assoc_array[$key]}"
done
这个脚本先获取关联数组的所有键,对键进行排序,然后按排序后的键输出键值对。
数组拼接
可以将两个或多个数组合并为一个数组。
- 索引数组合并:
array1=("apple" "banana")
array2=("cherry" "date")
combined_array=("${array1[@]}" "${array2[@]}")
echo ${combined_array[@]} # 输出 apple banana cherry date
- 关联数组合并(注意关联数组合并时,如果有相同键,后一个数组的键值对会覆盖前一个):
declare -A assoc_array1=( ["fruit1"]="apple" ["fruit2"]="banana" )
declare -A assoc_array2=( ["fruit2"]="cherry" ["fruit3"]="date" )
for key in ${!assoc_array2[*]}; do
assoc_array1[$key]=${assoc_array2[$key]}
done
for key in ${!assoc_array1[*]}; do
echo "Key: $key, Value: ${assoc_array1[$key]}"
done
这里先遍历 assoc_array2
,将其键值对合并到 assoc_array1
中,相同键的情况下会覆盖 assoc_array1
中的值。
数组查找
- 在索引数组中查找元素:
my_array=("apple" "banana" "cherry")
element="banana"
found=false
for value in ${my_array[@]}; do
if [ "$value" = "$element" ]; then
found=true
break
fi
done
if $found; then
echo "Element found"
else
echo "Element not found"
fi
这个脚本通过遍历索引数组,查找指定元素是否存在。
- 在关联数组中查找键或值:
declare -A my_assoc_array=( ["fruit1"]="apple" ["fruit2"]="banana" )
key="fruit2"
value="banana"
key_found=false
value_found=false
for k in ${!my_assoc_array[*]}; do
if [ "$k" = "$key" ]; then
key_found=true
fi
if [ "${my_assoc_array[$k]}" = "$value" ]; then
value_found=true
fi
done
if $key_found; then
echo "Key found"
else
echo "Key not found"
fi
if $value_found; then
echo "Value found"
else
echo "Value not found"
fi
这里分别通过遍历关联数组查找指定的键和值是否存在。
数组的嵌套
虽然 Bash 不直接支持多维数组,但可以通过数组的嵌套来模拟多维数组的行为。例如,我们可以创建一个数组,其每个元素又是一个数组。
# 初始化一个“二维”索引数组
array1=( ("apple" "red") ("banana" "yellow") )
# 访问“二维”数组元素
echo ${array1[0][0]} # 输出 apple
echo ${array1[0][1]} # 输出 red
对于关联数组的嵌套,原理类似,但实现起来可能更复杂一些,因为需要处理键值对的嵌套。
# 声明并初始化嵌套关联数组
declare -A outer_assoc
outer_assoc["fruit1"]=( ["name"]="apple" ["color"]="red" )
outer_assoc["fruit2"]=( ["name"]="banana" ["color"]="yellow" )
# 访问嵌套关联数组元素
echo ${outer_assoc["fruit1"]["name"]} # 输出 apple
echo ${outer_assoc["fruit2"]["color"]} # 输出 yellow
这里我们模拟了一个嵌套的关联数组,外层关联数组的每个值又是一个关联数组。
数组与函数
- 将数组作为参数传递给函数: 在 Bash 中,可以将数组作为参数传递给函数。但需要注意的是,传递时数组会被展开为独立的参数。
print_array() {
local arr=("$@")
for element in ${arr[@]}; do
echo $element
done
}
my_array=("apple" "banana" "cherry")
print_array "${my_array[@]}"
这里,print_array
函数接受一个数组作为参数,并输出数组的每个元素。
- 函数返回数组: 函数不能直接返回数组,但可以通过全局变量或输出结果再处理的方式来实现类似功能。
create_array() {
local -a result=("apple" "banana" "cherry")
echo ${result[@]}
}
returned_array=($(create_array))
for element in ${returned_array[@]}; do
echo $element
done
这个脚本中,create_array
函数创建一个数组并通过 echo
输出,主程序通过命令替换将输出转换为数组。
数组在实际脚本中的应用场景
- 文件处理: 假设我们有一个文本文件,每行包含一个文件名和文件大小,我们可以将这些信息读取到数组中进行处理。
#!/bin/bash
file_info=()
while read -r line; do
file_info+=("$line")
done < file_sizes.txt
for info in ${file_info[@]}; do
file=$(echo $info | awk '{print $1}')
size=$(echo $info | awk '{print $2}')
echo "File: $file, Size: $size"
done
这里,脚本从 file_sizes.txt
文件中读取每行信息,存储到 file_info
数组中,然后遍历数组处理文件名和文件大小。
- 系统监控: 在系统监控脚本中,可以使用数组来存储服务器的各种指标,如 CPU 使用率、内存使用率等。
#!/bin/bash
cpu_usage=()
mem_usage=()
for server in server1 server2 server3; do
cpu=$(ssh $server "top -bn1 | grep 'Cpu(s)' | awk '{print \$2}'")
mem=$(ssh $server "free -h | awk '/Mem:/ {print \$3}'")
cpu_usage+=("$cpu")
mem_usage+=("$mem")
done
for ((i = 0; i < ${#cpu_usage[@]}; i++)); do
echo "Server: server$((i + 1)), CPU Usage: ${cpu_usage[$i]}, Memory Usage: ${mem_usage[$i]}"
done
这个脚本通过 SSH 连接到多个服务器获取 CPU 和内存使用率,并存储在数组中,最后输出每个服务器的相关指标。
- 配置管理: 在配置管理脚本中,关联数组可以用来存储配置项及其值。例如,我们有一个数据库配置文件,其中包含数据库主机、端口、用户名和密码等信息。
#!/bin/bash
declare -A db_config
while read -r line; do
key=$(echo $line | cut -d'=' -f1)
value=$(echo $line | cut -d'=' -f2)
db_config["$key"]="$value"
done < db_config.txt
echo "Database Host: ${db_config["host"]}"
echo "Database Port: ${db_config["port"]}"
这里,脚本从 db_config.txt
文件中读取配置信息,存储到 db_config
关联数组中,然后可以方便地获取和使用这些配置值。
通过以上对 Bash 数组操作的详细介绍,包括定义、遍历、各种常用技巧以及实际应用场景,希望能帮助你在 Bash 脚本编程中更有效地使用数组,提升脚本的功能和效率。在实际编写脚本时,根据具体需求合理选择数组类型和操作方法,能够使代码更加简洁、易读和可维护。同时,不断实践和探索更多数组的高级应用,将有助于你成为一名更熟练的 Bash 脚本开发者。