Bash脚本中sed输出含\r导致for循环异常的排查求助
问题:处理配置文件时提取的段名带\r字符导致脚本异常
问题描述
在Ubuntu 20.04.2 LTS环境下,使用Bash脚本处理my.config配置文件时,通过sed提取的配置段名末尾带有\r(用cat -A查看显示为^M),导致后续for循环调用count_items_in_segment函数出现异常。目前临时通过${var: 0: -1}截断去除\r,需要找出问题根源并获得更合理的解决办法。
环境信息
- Kernel: 5.15.0-56-generic x86_64
- 架构: 64位
- 发行版: Ubuntu 20.04.2 LTS (Focal Fossa)
- Shell: zsh
相关脚本
#!/bin/bash input_file="my.config" function get_all_segment { echo `sed -n -E -e "/\[.*\]/p" $input_file | sed -E -e "s/\[|\]//g"` } function count_items_in_segment { sed -n "/\[$1\]/,/\[.*\]/p" $input_file | sed -n -E -e "/^#/d" -e "/^\s+$/d" -e "/\[.*\]/d" -e "p" | wc -l } index=1 for seg in `get_all_segment` do echo "$index: $seg" echo `count_items_in_segment $seg` ((index++)) done printf `get_all_segment`
配置文件内容
[client] database = test user = test password = pwd default-character-set = utf8 host = 192.168.0.1 port = 3306 # comment [client1] database = test default-character-set = utf8 host = 192.168.0.1 port = 3306 [client2] database = test user = test default-character-set = utf8 host = 192.168.0.1 port = 3306 [client3] database = test
问题根源
这个\r是Windows风格换行符(CRLF,即\r\n)中的回车字符。你的配置文件大概率是在Windows环境下创建或编辑过的,而Linux系统默认使用LF(\n)作为换行符,所以Linux会把\r当成普通字符保留在文件中,最终被sed提取出来。
解决办法
1. 永久转换文件为Linux换行格式
直接修改配置文件,将Windows风格的CRLF转换为Linux风格的LF:
- 使用
dos2unix工具(未安装可执行sudo apt install dos2unix):dos2unix my.config - 无
dos2unix时,用sed批量去除每行末尾的\r:sed -i 's/\r$//' my.config
2. 在提取段名时实时去除\r
修改get_all_segment函数,合并sed操作并同时去除\r,避免多余的命令替换:
function get_all_segment { sed -n -E '/\[.*\]/s/\[|\]|\r//gp' "$input_file" }
3. 优化脚本的可靠性(可选)
原脚本用for seg in get_all_segment``的方式会导致空格合并,改用while read循环更可靠;同时用awk替代多个sed+wc组合,提升效率:
#!/bin/bash input_file="my.config" function get_all_segment { sed -n -E '/\[.*\]/s/\[|\]|\r//gp' "$input_file" } function count_items_in_segment { awk -v seg="$1" ' $0 == "[" seg "]" { in_segment=1; next } /^\[.*\]/ { in_segment=0 } in_segment && !/^#/ && !/^\s*$/ { count++ } END { print count+0 } ' "$input_file" } index=1 get_all_segment | while read -r seg do echo "$index: $seg" count_items_in_segment "$seg" ((index++)) done
内容的提问来源于stack exchange,提问作者L.J.Zeng
相关产品推荐
相关产品推荐

