Shell文件读取与数组遍历需避坑:用while IFS= read -r line处理文件,避免for in $(cat)分词错误;数组操作须用"${arr[@]}"而非"${arr[*]}",初始化hosts=()后用hosts+=("$line")追加,遍历时用"${!hosts[@]}"获取下标。

Shell里按行读取文件和遍历数组,关键不在“会不会写”,而在“会不会避坑”。空格、换行、注释、特殊字符——这些日常出现的细节,一不留神就让脚本行为错乱甚至中断执行。
读取文件内容:优先用 while read 逐行处理
别用 for line in $(cat file),它会把每行按空格/制表符/换行符切开,遇到 web server01 # prod 这类带注释或空格的行,server01 和 # 就被拆成两个独立项,逻辑全乱。
-
推荐写法:用
while IFS= read -r line,配合重定向 -
IFS=防止行首尾空格被裁掉 -
-r防止反斜杠被当转义符误处理 - 加
[[ -n "$line" && "$line" != "#"* ]]可跳过空行和注释行
构建主机或配置数组:从文件加载后下标遍历更可控
读完文件不急着直接循环,先存进数组,后续既能按顺序访问,也能查长度、取指定索引、做条件筛选。
- 初始化空数组:
hosts=() - 逐行追加(带过滤):
hosts+=("$line") - 遍历时用
"${!hosts[@]}"获取所有下标,再通过${hosts[i]}取值,避免因元素含空格导致的分词问题 - 需要调试时,
echo "${hosts[@]}"或printf "[%d] %s\n" "${!hosts[@]}" "${hosts[@]}"能清晰看到结构
遍历数组:必须用 ${array[@]},别碰 ${array[*]}
${array[@]} 把每个元素当独立参数传给 for;${array[*]} 把全部元素拼成一个字符串,中间塞空格——一旦数组里有 "my report.pdf",ls "$f" 就会变成 ls "my" "report.pdf",命令直接报错。
- 安全遍历写法:
for f in "${files[@]}"; do ls -l "$f"; done - 如果要按序号处理,用 C 风格:
for ((i=0; i<${#files[@]}; i++)); do echo "$i: ${files[i]}"; done - 想跳过某几项?加
[[ $i == 1 || $i == 3 ]] && continue
动态生成数组:用 glob 或 find + mapfile,避开 ls 解析陷阱
files=($(ls *.log)) 是典型反模式:文件名含空格、括号、星号时,shell 会二次分词,结果不可控。
- 纯匹配当前目录文件:直接用
files=( *.log )—— glob 展开天然支持空格,且没子进程开销 - 需递归或加条件(如修改时间):
mapfile -t files < <(find . -name "*.log" -print0 | xargs -0 printf "%s\n") - 或更简洁:
readarray -t files < <(find . -name "*.log" -print0 | xargs -0 echo)(Bash 4.0+)


















