文章目录

Shell 脚本数组:@() 数组与索引

发布于 2026-04-06 04:07:59 · 浏览 14 次 · 评论 0 条

Shell 脚本数组:@() 数组与索引

Shell 脚本编程中,数组是一个极其强大但常被忽视的工具。掌握数组的用法,能让你的脚本从"单行命令"进化成"自动化利器"。本文将深入讲解 Bash 中 @() 数组的创建、索引机制以及常用操作手法。


一、认识 Shell 数组

在 Bash 中,数组是一组按顺序排列的数据集合。与其他编程语言不同,Bash 数组不需要预先声明大小,可以在运行时动态扩展。数组中的每个元素都有一个唯一的编号——称为索引,通过索引可以快速定位和操作具体元素。

Shell 数组的核心特点是索引从 0 开始,这一点与其他主流语言一致。理解索引机制是掌握数组的关键,后续所有的操作都围绕索引展开。


二、创建数组的基本方法

1. 直接赋值法

创建数组最简单的方式是直接给变量名加中括号并赋值。每个元素之间用空格分隔:

# 创建包含三个元素的数组
fruits[0]="apple"
fruits[1]="banana"
fruits[2]="orange"

2. @() 快捷语法

@() 是 Bash 提供的数组字面量语法,一行代码即可完成数组创建。这种方式更加简洁直观,是日常使用的主流方法:

# 使用 @() 创建数组
fruits=("apple" "banana" "orange")

执行后,fruits 数组自动包含三个元素:索引 0 对应 "apple",索引 1 对应 "banana",索引 2 对应 "orange"。

3. 混合初始化

可以混用直接赋值和 @() 语法,实现灵活的数组构建:

# 先用 @() 创建基础数组,再用单独赋值追加
servers=("web01" "web02" "db01")
servers[3]="cache01"
servers[4]="backup01"

三、数组索引的核心规则

1. 索引的基本特性

Shell 数组的索引规则简单明确,理解这些规则能避免大多数常见错误:

特性 说明
起始索引 数组索引从 0 开始,而非 1
连续性 索引必须为整数,可以不连续
最大索引 受限于系统内存和整数范围
负数索引 Bash 4.3+ 支持负数索引,从末尾倒数

2. 查看数组长度与索引范围

获取数组信息是调试和编程中的常见需求,以下命令可以快速获取关键信息:

# 获取数组元素个数
echo ${#fruits[@]}    # 输出:3

# 获取某个元素的字符长度
echo ${#fruits[0]}    # 输出:5 (apple 的长度)

# 列出所有索引
echo ${!fruits[@]}    # 输出:0 1 2

# 获取所有元素值
echo ${fruits[@]}     # 输出:apple banana orange

3. 访问单个元素

通过 `${数组名[索引]}` 语法访问指定元素,不指定索引时默认访问索引 0: ```bash # 访问索引 1 的元素 echo ${fruits[1]} # 输出:banana

访问索引 0(默认行为)

echo ${fruits} # 输出:apple # 访问不存在的索引返回空字符串 echo ${fruits[99]} # 输出:(空)


### 4. 访问多个元素(切片操作)

Bash 支持使用通配符语法进行数组切片,一次性获取多个连续元素:

```bash
fruits=("apple" "banana" "orange" "grape" "melon")

# 从索引 1 开始,取 2 个元素
echo ${fruits[@]:1:2}     # 输出:banana orange

# 从索引 0 开始,取到末尾
echo ${fruits[@]:0}       # 输出全部元素

# 从倒数第 2 个开始,取 1 个
echo ${fruits[@]: -2:1}   # 输出:grape
```

注意切片语法中的空格:`:` 后面有一个空格,`-2` 与 `:` 之间也有空格,这些空格是语法的必要组成部分。

---

## 四、数组的增删改操作

### 1. 修改元素值

直接对指定索引赋值即可修改元素,数组会自动更新:

```bash
fruits=("apple" "banana" "orange")

# 修改索引 1 的元素
fruits[1]="pear"

echo ${fruits[@]}    # 输出:apple pear orange

2. 追加新元素

在数组末尾添加元素有两种常用方式,推荐使用 += 运算符:

fruits=("apple" "banana")

# 方式一:使用 += 运算符(推荐)
fruits+=("cherry" "date")

# 方式二:使用索引赋值
fruits[${#fruits[@]}]="elderberry"

echo ${fruits[@]}    # 输出:apple banana cherry date elderberry

3. 删除元素

使用 unset 命令删除指定索引的元素,删除后数组会出现"空洞"——索引不再连续:

fruits=("apple" "banana" "orange" "grape")

# 删除索引 2 的元素
unset fruits[2]

# 查看结果
echo ${fruits[@]}    # 输出:apple banana grape
echo ${!fruits[@]}   # 输出:0 1 3 (索引 2 消失了)

# 删除整个数组
unset fruits

删除元素后,原有索引会被释放,后续插入新元素时可能复用这些索引。

4. 插入元素到指定位置

在中间位置插入元素需要重新构建数组:

fruits=("apple" "banana" "orange")

# 在索引 1 位置插入 "mango"
fruits=("${fruits[0]}" "mango" "${fruits[@]:1}")

echo ${fruits[@]}    # 输出:apple mango banana orange
```

---

## 五、数组遍历方法

遍历数组是脚本中最常见的操作之一,Bash 提供了多种遍历语法。

### 1. for 循环遍历(基础用法)

直接遍历数组元素是最简单的方式:

```bash
fruits=("apple" "banana" "orange")

for fruit in "${fruits[@]}"; do
    echo "水果: $fruit"
done
```

输出结果逐行显示每个元素值。

### 2. for 循环遍历(索引版)

需要知道当前元素索引时,使用索引遍历:

```bash
fruits=("apple" "banana" "orange")

for i in "${!fruits[@]}"; do
    echo "索引 $i 的值是: ${fruits[$i]}"
done
```

这种方式的优点是可以同时获取索引和值,适用于需要按索引处理数据的场景。

### 3. C 风格 for 循环

对于需要计数器控制的场景,可以使用 C 语言风格的循环:

```bash
fruits=("apple" "banana" "orange")

for ((i=0; i<${#fruits[@]}; i++)); do
    echo "fruits[$i] = ${fruits[$i]}"
done
```

这种方式在处理复杂逻辑时更加灵活。

### 4. while 循环遍历

结合索引自增实现 while 循环遍历:

```bash
fruits=("apple" "banana" "orange")
i=0

while [ $i -lt ${#fruits[@]} ]; do
    echo "fruits[$i] = ${fruits[$i]}"
    ((i++))
done

六、实战场景与技巧

1. 批量处理文件

数组非常适合批量管理文件路径或名称:

# 收集指定类型的文件
logs=(/var/log/*.log)

# 检查每个日志文件大小
for log in "${logs[@]}"; do
    if [ -f "$log" ]; then
        size=$(wc -c < "$log")
        echo "$log: $size bytes"
    fi
done

2. 字符串转数组

将字符串按指定分隔符拆分为数组,是处理日志、CSV 等数据的常用技巧:

# 定义逗号分隔的字符串
data="server01,server02,server03"

# 按逗号转换为数组
IFS=',' read -ra servers <<< "$data"

for server in "${servers[@]}"; do
    echo "服务器: $server"
done
```

关键在于设置 `IFS`(内部字段分隔符),使其与分隔符匹配,然后用 `read -ra` 将字符串读取到数组中。

### 3. 数组去重

从数组中提取唯一值可用于数据清洗:

```bash
# 原始数组含重复值
items=("a" "b" "a" "c" "b" "d")

# 使用关联数组去重
declare -A seen
unique=()

for item in "${items[@]}"; do
    if [[ -z "${seen[$item]}" ]]; then
        seen[$item]=1
        unique+=("$item")
    fi
done

echo "${unique[@]}"    # 输出:a b c d
```

### 4. 数组排序

对数组元素进行排序是常见需求,可以使用 `sort` 命令配合数组操作:

```bash
# 待排序数组
numbers=(5 2 8 1 9 3)

# 排序并转换回数组
sorted=($(printf '%s\n' "${numbers[@]}" | sort -n))

echo "${sorted[@]}"    # 输出:1 2 3 5 8 9

5. 数组交集与差集

处理集合运算时,数组可以派上用场:

arr1=("a" "b" "c" "d")
arr2=("c" "d" "e" "f")

# 计算交集
commom=()
for item in "${arr1[@]}"; do
    for target in "${arr2[@]}"; do
        if [ "$item" = "$target" ]; then
            common+=("$item")
            break
        fi
    done
done

echo "交集: ${common[@]}"    # 输出:c d

七、常见陷阱与注意事项

1. 索引越界

访问不存在的索引不会报错,而是返回空字符串,这可能导致逻辑错误:

fruits=("apple" "banana")
echo ${fruits[10]}    # 空输出,无错误提示

# 安全做法:先检查索引是否存在
if [ $index -lt ${#fruits[@]} ]; then
    echo ${fruits[$index]}
else
    echo "索引超出范围"
fi
```

### 2. 引用与不加引号的区别

数组展开时是否加引号会导致截然不同的结果:

```bash
fruits=("apple banana" "orange")

# 加引号:保留元素内的空格
echo "${fruits[@]}"    # 输出:apple banana orange (两个独立元素)

# 不加引号:空格会分割元素
echo ${fruits[@]}      # 输出:apple banana orange (三个元素)
```

**始终在数组引用时使用引号**,这是防止因空格导致分割错误的关键原则。

### 3. 稀疏数组的索引问题

删除中间元素后,数组索引不再连续,遍历时需要注意:

```bash
fruits=("a" "b" "c" "d")
unset fruits[1]    # 删除索引 1

# 直接遍历会跳过空洞
for fruit in "${fruits[@]}"; do
    echo $fruit    # 输出:a c d
done

# 遍历索引才能看到全部(包括空洞位置)
for i in "${!fruits[@]}"; do
    echo "索引 $i: ${fruits[$i]}"
done
# 输出:索引 0: a,索引 2: c,索引 3: d
```

---

## 八、关联数组(补充知识点)

标准数组使用整数索引,Bash 4.0+ 支持关联数组,可以使用字符串作为键:

```bash
# 声明关联数组
declare -A user_info

# 使用字符串键赋值
user_info["name"]="张三"
user_info["age"]="30"
user_info["city"]="北京"

# 访问元素
echo "姓名: ${user_info[name]}"
echo "年龄: ${user_info[age]}"

# 列出所有键
echo "所有键: ${!user_info[@]}"

关联数组在需要键值映射的场景下非常实用,如配置管理、缓存映射等。


总结

Shell 数组的核心在于索引机制。掌握 @() 语法创建数组、理解从 0 开始的索引规则、熟悉增删改查操作,就能高效处理批量数据。记住三个要点:遍历时加引号保护空格、访问前检查索引边界、善用切片和追加操作提升代码简洁性。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文