关键在于控制shell字符串切分方式,应将ifs设为换行符使每行整体处理,或用while read逐行读取避免ifs影响。

关键在于控制 shell 如何切分字符串——默认用空格、制表符和换行符分割,所以含空格的文本一读就碎。解决思路不是“绕开空格”,而是明确告诉 shell:按行切,别按空格切。
临时修改 IFS 为换行符
这是最直接、最常用的方法。IFS(Internal Field Separator)决定单词怎么拆,把它设成换行符,就能让每行当一个整体处理:
- 先备份原始 IFS:OLDIFS=$IFS
- 设为仅换行:IFS=$'\n'(推荐写法,清晰安全)
- 再执行 for 循环:for line in $(cat file.txt); do echo "$line"; done
- 最后还原:IFS=$OLDIFS(避免影响后续命令)
用 while read 替代 for(更健壮)
for + $(cat ...) 本质是先展开再分割,仍有隐患;而 while read 是逐行读取,天然不依赖 IFS,对空格、制表符、开头缩进都友好:
- 基本写法:while IFS= read -r line; do echo "$line"; done
- IFS= 防止行首尾空白被裁掉
- -r 防止反斜杠被转义(比如路径里的 \n 不会被误解析)
- 适合处理日志、配置、用户输入等不可控格式的文本
数组方式预加载(适合小文件或已知内容)
如果文本行数不多,可先读入数组,再遍历。数组元素本身保留原始边界,只要声明时加引号,空格就安全:
- 读入:mapfile -t lines (-t 去掉每行末尾换行符)
- 遍历:for line in "${lines[@]}"; do echo "$line"; done
- 注意必须用 "${lines[@]}",不能漏引号,否则又会二次拆分
避免常见陷阱
有些写法看似省事,实际埋雷:
- ❌ for line in `cat file.txt` —— 反引号无引号包裹,空格照拆
- ❌ for file in *.log —— 文件名带空格时,* 展开后仍受 IFS 影响,应改用 find ... -print0 | while IFS= read -r -d '' file
- ❌ 忘记还原 IFS 或漏引号 —— 会导致后续脚本行为异常,尤其在函数中使用时











