数组:索引数组、关联数组 (Arrays)

章节概述

本章讲解 Bash 数组的两种类型:索引数组(整数下标)和关联数组(字符串键),以及数组的声明、赋值、遍历、切片、追加等操作。通过与 C 数组的对比,理解 Bash 数组的动态特性和使用限制。

核心理念:Bash 数组是动态的、稀疏的、无类型的——你可以随时添加元素,下标可以不连续,同一个数组可以混合存储字符串和数字。


第1节:索引数组声明与赋值

# 方式1:直接赋值
fruits=("apple" "banana" "cherry")
 
# 方式2:逐个赋值
colors[0]="red"
colors[1]="green"
colors[2]="blue"
 
# 方式3:声明后赋值
declare -a nums
nums=(10 20 30 40 50)
 
# 访问元素
echo ${fruits[0]}    # apple
echo ${fruits[2]}    # cherry
 
# 修改元素
fruits[1]="blueberry"

稀疏数组:

# 下标可以不连续
sparse[0]="zero"
sparse[5]="five"
sparse[10]="ten"
echo ${#sparse[@]}    # 3(只有3个元素)

C 数组对比:

// C:固定大小、固定类型、连续内存
int nums[5] = {10, 20, 30, 40, 50};
char *fruits[] = {"apple", "banana", "cherry"};
 
// 访问
printf("%d\n", nums[0]);     // 10
printf("%s\n", fruits[1]);   // banana
# Bash:动态、无类型、可稀疏
nums=(10 20 30 40 50)
fruits=("apple" "banana" "cherry")
mixed=("hello" 42 3.14)    # 可混合类型
 
echo ${nums[0]}        # 10
echo ${fruits[1]}      # banana
echo ${mixed[2]}       # 3.14

第2节:数组长度与元素

arr=("a" "b" "c" "d" "e")
 
echo ${#arr[@]}      # 5(元素个数)
echo ${#arr[0]}      # 1(第一个元素的长度)

获取所有元素:

arr=("a" "b" "c")
 
# ${arr[@]} 和 ${arr[*]} 的区别
echo ${arr[@]}       # a b c(分别引用)
echo ${arr[*]}       # a b c(作为整体)
 
# 在 for 循环中必须用 "${arr[@]}"(加引号)
for item in "${arr[@]}"; do
    echo "$item"
done

{arr[*]}:

语法含义用途
${arr[@]}各元素独立for 循环、传参
${arr[*]}拼接为一个字符串统计、打印
arr=("hello world" "foo bar")
 
# 加引号的区别
for x in "${arr[@]}"; do echo "[$x]"; done
# [hello world]
# [foo bar]
 
for x in ${arr[@]}; do echo "[$x]"; done
# [hello]
# [world]
# [foo]
# [bar]

第3节:数组遍历

# 遍历值
arr=("apple" "banana" "cherry")
for fruit in "${arr[@]}"; do
    echo "$fruit"
done
 
# 遍历索引
for i in "${!arr[@]}"; do
    echo "索引 $i: ${arr[$i]}"
done
 
# C 风格遍历
for ((i=0; i<${#arr[@]}; i++)); do
    echo "${arr[$i]}"
done

第4节:数组追加与删除

arr=("a" "b" "c")
 
# 追加元素
arr+=("d" "e")
echo ${arr[@]}    # a b c d e
 
# 删除元素(留下空洞)
unset arr[1]      # 删除 "b"
echo ${arr[@]}    # a c d e
echo ${#arr[@]}   # 4

追加 vs 赋值:

arr=(1 2 3)
arr+=(4 5)        # 追加
echo ${arr[@]}    # 1 2 3 4 5
 
arr=(1 2 3)
arr[10]=100       # 赋值到指定位置
echo ${arr[@]}    # 1 2 3 100(稀疏)
echo ${#arr[@]}   # 4

第5节:数组切片

arr=("a" "b" "c" "d" "e")
 
echo ${arr[@]:1:3}    # b c d(从索引1取3个)
echo ${arr[@]:2}      # c d e(从索引2到末尾)
echo ${arr[@]::3}     # a b c(前3个)

切片语法:

语法说明
${arr[@]:pos}从 pos 到末尾
${arr[@]:pos:len}从 pos 取 len 个
${arr[@]::len}前 len 个

第6节:关联数组(Bash 4+)

# 必须用 declare -A 声明
declare -A ages
ages[alice]=25
ages[bob]=30
ages[charlie]=35
 
# 声明时赋值
declare -A capitals=(
    [China]="Beijing"
    [Japan]="Tokyo"
    [France]="Paris"
)
 
echo ${ages[alice]}        # 25
echo ${capitals[Japan]}    # Tokyo

关联数组遍历:

declare -A scores=(
    [math]=95
    [english]=88
    [science]=92
)
 
# 遍历键
for subject in "${!scores[@]}"; do
    echo "$subject: ${scores[$subject]}"
done
 
# 遍历值
for score in "${scores[@]}"; do
    echo "$score"
done

索引 vs 关联数组:

特性索引数组关联数组
声明arr=()declare -adeclare -A map
下标整数字符串
顺序按索引按哈希(无序)
Bash 版本所有版本4.0+

第7节:数组作为函数参数

# 传递数组给函数
print_array() {
    local arr=("$@")
    for item in "${arr[@]}"; do
        echo "$item"
    done
}
 
fruits=("apple" "banana" "cherry")
print_array "${fruits[@]}"

数组切片传参:

# 传递部分数组
subarray=("${fruits[@]:1:2}")
print_array "${subarray[@]}"    # banana cherry

第8节:实战 —— 简易学生管理系统

#!/usr/bin/env bash
declare -A students
 
students[1001]="Alice:95"
students[1002]="Bob:88"
students[1003]="Charlie:92"
 
echo "学号   姓名     成绩"
echo "----   ----     ----"
for id in $(echo "${!students[@]}" | tr ' ' '\n' | sort); do
    IFS=':' read -r name score <<< "${students[$id]}"
    printf "%-6s %-8s %s\n" "$id" "$name" "$score"
done

第9节:课后练习

  1. 创建关联数组存储 5 种水果及其价格
  2. 编写函数接收数组参数并返回所有偶数元素
  3. 实现简单的键值对配置文件解析器

相关章节: