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保证安全读取 - 数组循环可防止空格拆分
- 自动跳过当前目录
. - 脚本健壮、可直接上线