Shell脚本按冒号分隔读取txt文件时仅遍历首行就终止的问题
问题原因
该问题90%以上由文本文件格式不兼容导致,核心触发点如下:
- 你的
users_test.txt是在Windows环境下创建/编辑的,采用DOS/Windows标准的CRLF(\r\n)作为换行符,且第一行末尾携带了DOS系统特有的文件结束标记Ctrl+Z(十六进制0x1A)。Bash读取文件时遇到这个标记会直接判定到达文件末尾(EOF),不会继续读取第二行的Group_B内容。 - 行尾携带的
\r(回车符)会被带入读取到的字段变量中,输出时\r会让终端光标回到当前行的起始位置,后续输出的内容会覆盖前面已经打印的字符,这也正好解释了你看到的异常输出:第一行本该打印group Group_A has teams...,但Group_A被回车后输出的内容覆盖,最终显示为group has teams...。 - 额外的兼容性问题:如果文件最后一行没有尾随的换行符,原生
read命令读到内容时会返回非0退出码,直接终止循环,导致最后一行被跳过。
修复步骤
先把文本文件转成Unix格式,清除Windows格式的控制字符:
如果你安装了dos2unix工具,直接执行:dos2unix users_test.txt没有该工具的话用sed替换行尾回车符即可:
sed -i 's/\r$//' users_test.txt可以用
cat -A users_test.txt检查文件格式,正常Unix格式的行尾只会显示$,不会出现^M(代表\r)、^Z(代表DOS EOF标记)这类特殊字符。优化循环的兼容逻辑,修改while判断行,兼容最后一行无换行的场景:
# 加 || [[ -n "$groups" ]] 是为了避免最后一行无换行时被跳过 while IFS=':' read -r groups teams users || [[ -n "$groups" ]]修正脚本里不规范的变量引用写法,所有变量放在双引号内部,避免意外分词导致的异常,比如把
echo "hey: "$team统一改成echo "hey: $team"的格式。
修复后的完整参考脚本
#!/bin/bash FILENAME="users_test.txt" while IFS=':' read -r groups teams users || [[ -n "$groups" ]] do echo "group $groups has teams $teams and user $users" for team in $teams; do echo "hey: $team" done for group in $groups; do echo "hi: $group" done for user in $users; do echo "hello: $user" done done < "$FILENAME"
内容的提问来源于stack exchange,提问作者Zizi96
相关产品推荐
相关产品推荐

