MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Bash数组操作:定义、遍历与常用技巧

2021-06-254.2k 阅读

Bash 数组基础:定义与初始化

定义索引数组

在 Bash 中,定义索引数组非常简单。索引数组的索引从 0 开始,就像许多其他编程语言一样。可以使用以下几种方式来定义和初始化一个索引数组。

  1. 逐个赋值
my_array[0]="apple"
my_array[1]="banana"
my_array[2]="cherry"

在这个例子中,我们分别为数组 my_array 的 0、1、2 索引位置赋值。

  1. 一次性赋值
my_array=("apple" "banana" "cherry")

这种方式更加简洁,直接在定义数组时就为其填充了元素。这里,数组的索引会自动从 0 开始顺序分配。

  1. 指定索引赋值
my_array=( [3]="date" [5]="fig" )

在这个例子中,我们跳过了索引 0 - 2 和 4,直接为索引 3 和 5 赋值。未赋值的索引位置的值为空。

定义关联数组

关联数组(也称为哈希表或字典)在 Bash 中从版本 4.0 开始支持。与索引数组不同,关联数组使用任意的字符串作为索引。

  1. 启用关联数组支持: 在使用关联数组之前,需要先声明要使用关联数组类型,通过 declare -A 命令来实现。
declare -A my_assoc_array
  1. 逐个赋值
my_assoc_array["fruit1"]="apple"
my_assoc_array["fruit2"]="banana"

这里我们为关联数组 my_assoc_array 分别设置了键为 fruit1fruit2 的值。

  1. 一次性赋值
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

数组遍历

遍历索引数组

  1. 使用 for 循环遍历索引
my_array=("apple" "banana" "cherry")
for i in ${!my_array[*]}; do
    echo "Index $i: ${my_array[$i]}"
done

在这个脚本中,${!my_array[*]} 返回数组 my_array 的所有索引。for 循环遍历这些索引,并输出每个索引及其对应的值。

  1. 使用 for 循环遍历元素
my_array=("apple" "banana" "cherry")
for element in ${my_array[@]}; do
    echo "Element: $element"
done

这里,for 循环直接遍历数组的元素并输出。

遍历关联数组

  1. 使用 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 循环遍历这些键,并通过键获取对应的值输出。

数组常用操作技巧

获取数组长度

  1. 索引数组长度: 对于索引数组,可以使用 # 符号结合数组名和通配符来获取数组的长度。
my_array=("apple" "banana" "cherry")
length=${#my_array[*]}
echo "Array length: $length"  # 输出 Array length: 3
  1. 关联数组长度: 获取关联数组长度的方法与索引数组类似。
declare -A my_assoc_array=( ["fruit1"]="apple" ["fruit2"]="banana" )
length=${#my_assoc_array[*]}
echo "Assoc Array length: $length"  # 输出 Assoc Array length: 2

添加元素到数组

  1. 向索引数组添加元素: 要向索引数组末尾添加元素,可以使用 += 操作符。
my_array=("apple" "banana")
my_array+=("cherry")
echo ${my_array[@]}  # 输出 apple banana cherry
  1. 向关联数组添加元素: 向关联数组添加元素只需为新的键赋值即可。
declare -A my_assoc_array=( ["fruit1"]="apple" )
my_assoc_array["fruit2"]="banana"

删除数组元素

  1. 删除索引数组元素: 使用 unset 命令可以删除数组中的元素。
my_array=("apple" "banana" "cherry")
unset my_array[1]
echo ${my_array[@]}  # 输出 apple cherry

这里删除了索引为 1 的元素。

  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 来实现。

  1. 索引数组排序
my_array=("cherry" "apple" "banana")
sorted_array=($(echo ${my_array[@]} | tr ' ' '\n' | sort))
echo ${sorted_array[@]}  # 输出 apple banana cherry

这里先将数组元素通过 tr 命令转换为每行一个元素,然后使用 sort 命令排序,最后重新构建数组。

  1. 关联数组排序(按键排序)
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

这个脚本先获取关联数组的所有键,对键进行排序,然后按排序后的键输出键值对。

数组拼接

可以将两个或多个数组合并为一个数组。

  1. 索引数组合并
array1=("apple" "banana")
array2=("cherry" "date")
combined_array=("${array1[@]}" "${array2[@]}")
echo ${combined_array[@]}  # 输出 apple banana cherry date
  1. 关联数组合并(注意关联数组合并时,如果有相同键,后一个数组的键值对会覆盖前一个)
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 中的值。

数组查找

  1. 在索引数组中查找元素
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

这个脚本通过遍历索引数组,查找指定元素是否存在。

  1. 在关联数组中查找键或值
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

这里我们模拟了一个嵌套的关联数组,外层关联数组的每个值又是一个关联数组。

数组与函数

  1. 将数组作为参数传递给函数: 在 Bash 中,可以将数组作为参数传递给函数。但需要注意的是,传递时数组会被展开为独立的参数。
print_array() {
    local arr=("$@")
    for element in ${arr[@]}; do
        echo $element
    done
}
my_array=("apple" "banana" "cherry")
print_array "${my_array[@]}"

这里,print_array 函数接受一个数组作为参数,并输出数组的每个元素。

  1. 函数返回数组: 函数不能直接返回数组,但可以通过全局变量或输出结果再处理的方式来实现类似功能。
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 输出,主程序通过命令替换将输出转换为数组。

数组在实际脚本中的应用场景

  1. 文件处理: 假设我们有一个文本文件,每行包含一个文件名和文件大小,我们可以将这些信息读取到数组中进行处理。
#!/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 数组中,然后遍历数组处理文件名和文件大小。

  1. 系统监控: 在系统监控脚本中,可以使用数组来存储服务器的各种指标,如 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 和内存使用率,并存储在数组中,最后输出每个服务器的相关指标。

  1. 配置管理: 在配置管理脚本中,关联数组可以用来存储配置项及其值。例如,我们有一个数据库配置文件,其中包含数据库主机、端口、用户名和密码等信息。
#!/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 脚本开发者。