SED提取*SET_PART开头段落异常:如何实现精准连续匹配
提取特定段落的文本处理需求与解决方案建议
需求说明
需要提取文本中所有以*SET_PART开头的段落,段落的结束标志是下一个以*开头的行(该*行是新段落的起始,不应包含在当前段落中,且要作为下一次搜索的起始点)。
当前使用的命令:
sed -n '/*SET_PART/,/*/p' Inputfile > Outputfile
输入文件(Inputfile)内容
*SET_PART_COLLECT_TITLE Gesamtfahrzeug 101 1005043 1005049 1005101 1005102 1005103 1005200 1005300 1005400 1005504 1005601 1005700 1005800 1005900 1006000 1006100 1006101 1006102 1006103 1006200 1006201 1006202 1006203 1006400 1006401 1006402 1006500 1006600 1006700 1006800 1006900 1007000 1007100 1007200 1007201 1007202 1007203 1007204 1007300 1007301 1007302 1007303 1007304 1007305 1007306 1007307 1007308 1007309 1007310 1007311 1007312 1007313 1007314 1007400 1007500 1007600 1007700 1007800 1007801 1015700 1015800 *Keyword 202 1000000 1002002 1002312 1002313 1002323 1002701 *SET_PART_COLLECT_TITLE Verbindungstechnik_Slave 201 1000000 1002002 1002312 1002313 1002323 1002701 *SET_PART_LIST_TITLE Bord_stein_hi_Forcetransducer 3 0. 0. 0. 0. 3 *SET_PART_COLLECT_TITLE *SET_PART_LIST_TITLE Fahrbahn_Forcetransducer 1 1 *END
当前命令的输出问题
执行上述命令后,会错误地包含结束标志行(比如*Keyword),且无法正确处理连续的*SET_PART段落,输出如下:
*SET_PART_COLLECT_TITLE Gesamtfahrzeug 101 1005043 1005049 1005101 1005102 1005103 1005200 1005300 1005400 1005504 1005601 1005700 1005800 1005900 1006000 1006100 1006101 1006102 1006103 1006200 1006201 1006202 1006203 1006400 1006401 1006402 1006500 1006600 1006700 1006800 1006900 1007000 1007100 1007200 1007201 1007202 1007203 1007204 1007300 1007301 1007302 1007303 1007304 1007305 1007306 1007307 1007308 1007309 1007310 1007311 1007312 1007313 1007314 1007400 1007500 1007600 1007700 1007800 1007801 1015700 1015800 *Keyword *SET_PART_COLLECT_TITLE Verbindungstechnik_Slave 201 1000000 1002002 1002312 1002313 1002323 1002701 *SET_PART_LIST_TITLE *SET_PART_COLLECT_TITLE *SET_PART_LIST_TITLE
期望输出
准确提取所有*SET_PART开头的段落,不包含后续的非*SET_PART起始行,且连续的*SET_PART段落都能被正确提取:
*SET_PART_COLLECT_TITLE Gesamtfahrzeug 101 1005043 1005049 1005101 1005102 1005103 1005200 1005300 1005400 1005504 1005601 1005700 1005800 1005900 1006000 1006100 1006101 1006102 1006103 1006200 1006201 1006202 1006203 1006400 1006401 1006402 1006500 1006600 1006700 1006800 1006900 1007000 1007100 1007200 1007201 1007202 1007203 1007204 1007300 1007301 1007302 1007303 1007304 1007305 1007306 1007307 1007308 1007309 1007310 1007311 1007312 1007313 1007314 1007400 1007500 1007600 1007700 1007800 1007801 1015700 1015800 *SET_PART_COLLECT_TITLE Verbindungstechnik_Slave 201 1000000 1002002 1002312 1002313 1002323 1002701 *SET_PART_LIST_TITLE Bord_stein_hi_Forcetransducer 3 0. 0. 0. 0. 3 *SET_PART_COLLECT_TITLE *SET_PART_LIST_TITLE Fahrbahn_Forcetransducer 1 1
解决方案
改进的sed命令
利用sed的分支和标记功能实现需求,避免包含结束行,并支持连续段落:
sed -n '/\*SET_PART/{:a;N;/\n\*/{s/\n\*.*//;p;b};ba}' Inputfile > Outputfile
解释:
/\*SET_PART/:匹配到*SET_PART行时进入处理流程:a:定义循环标记aN:读取下一行到模式空间/\n\*/:检查是否遇到以*开头的行- 匹配成功:删除该
*行及其换行符,打印结果后跳出循环 - 匹配失败:跳回标记
a继续读取下一行
- 匹配成功:删除该
更合适的工具:awk
awk的段落处理逻辑更直观,代码可读性更强:
awk '/^\*/ { if (in_part) { print buffer buffer = "" } if ($0 ~ /\*SET_PART/) { in_part = 1 buffer = $0 "\n" } else { in_part = 0 } next } in_part { buffer = buffer $0 "\n" } END { if (in_part) print buffer }' Inputfile > Outputfile
解释:
- 遇到以
*开头的行时:- 如果正在处理
*SET_PART段落,先打印之前缓存的内容 - 判断当前行是否是
*SET_PART开头,是则开启处理标记并缓存该行,否则关闭标记
- 如果正在处理
- 开启处理标记时,将每行内容加入缓存
- 文件处理结束时,打印剩余的缓存内容
环境说明
使用的Gnome版本为3.32.2,上述工具(sed、awk)均为Linux系统默认自带工具,无需额外安装。
内容的提问来源于stack exchange,提问作者kmarc87
相关产品推荐
相关产品推荐

