Shell 数组与 for 循环最佳实践

Shell 数组和 for 循环的最佳实践在于确保代码的健壮性 (Robustness),尤其是在处理包含空格或特殊字符的数组元素时。

🧩 一、定义数组的标准写法

1# ✅ 正确:使用括号 + 双引号,保证空格安全
2dirs=(
3  "/opt/app/logs"
4  "/opt/app data"
5  "/tmp/test"
6)

❌ 错误写法:

1dirs=/opt/app/logs /opt/app data /tmp/test

这会被当成多个命令执行。


🔁 二、安全遍历数组

✅ 推荐写法

1for dir in "${dirs[@]}"; do
2  echo "Processing directory: $dir"
3done
  • ${dirs[@]}:展开数组中每个元素为独立参数(保留空格)。
  • 双引号 "${dirs[@]}":防止被空格拆分。
  • 永远加双引号 —— 这是 Bash 的黄金定律。

🧠 三、普通 for 遍历(非数组)

1for file in *.log; do
2  echo "Found file: $file"
3done

✅ 特点:

  • 可用于通配符匹配(*.log

  • 直接处理命令输出:

    1for line in $(cat /etc/hosts); do
    2    echo "Host: $line"
    3done
    

🚫 缺点:

  • 空格会被拆开,"foo bar""foo" + "bar"
  • 不适合结构化或复杂数据

⚙️ 四、带索引的数组遍历

1for i in "${!dirs[@]}"; do
2  echo "Index: $i → Dir: ${dirs[$i]}"
3done
  • ${!dirs[@]} 表示数组的所有索引。
  • 适合需要根据位置做映射或判断的场景。

🚫 五、常见陷阱与错误示例

错误代码问题修正方法
for x in ${arr[@]}被空格拆开for x in "${arr[@]}"
for x in $(ls)文件名带空格时出错for x in *; do
忘记 IFS 处理输入分隔符错误临时修改 IFS=$'\n'

🔒 六、安全处理命令输出(带空格)

有些命令输出路径中带空格,例如:

1find /data -type d

使用 mapfile / readarray 最安全:

1mapfile -t dirs < <(find /data -type d)
2
3for dir in "${dirs[@]}"; do
4  echo "Found dir: $dir"
5done

mapfile -t 自动按行读取输出,不会被空格拆开。


🧹 七、IFS(Internal Field Separator)

默认 IFS 是空格、Tab、换行。 如果要按“行”遍历输出,可以这样:

1IFS=$'\n' read -r -d '' -a lines < <(ls -1)
2for line in "${lines[@]}"; do
3  echo "Line: $line"
4done

🧰 八、综合实战模板(含数组 + 动态命令)

 1#!/bin/bash
 2set -euo pipefail
 3
 4# 定义要保留的文件夹天数
 5keep_days=3
 6
 7# 找出三天前的目录名
 8mapfile -t old_dirs < <(find . -maxdepth 1 -type d -name "20*" -mtime +$keep_days)
 9
10echo "🧹 Found ${#old_dirs[@]} old directories to delete"
11
12for dir in "${old_dirs[@]}"; do
13  echo "Deleting: $dir"
14  rm -rf "$dir"
15done

✅ 特点:

  • 使用 mapfile 保证安全读取
  • 数组循环可防止空格拆分
  • 自动跳过当前目录 .
  • 脚本健壮、可直接上线