Bash文本处理:仅删除$coord区块内行尾的f字符
问题描述
我有一个名为coord的大型文件,内容结构示例如下:
$coord -6.81387808414325 5.82189470091282 -1.45477353169903 c f 3.12250219010826 1.39239934150351 0.78451413146001 o f -4.76572488013335 -1.67551810949494 -1.58797087759328 c f -0.15061495158492 -2.18614667480844 -2.60227003662941 c f etc... 9.21060449992324 -2.77968508411378 0.71587738888748 h f 5.87109372056745 -2.67040600177892 0.54514819243204 h f 7.70747476642116 -1.85827163328137 -2.12317155170529 h f 3.16053583847830 1.75657003778612 4.21784993053015 h 3.20523873898751 2.06642906155866 6.03962166222879 o 3.84518636016769 0.52341324778083 6.76769535558585 h $intdef # definitions of internal coordinates 1 f 1.0000000000000 stre 6 21 val= 2.05908 2 f 1.0000000000000 stre 6 53 val= 2.07110 3 f 0.0463401612403 bend 53 21 6 val= 1.20720 0.5016372600998 bend 7 21 6 0.4983829790270 bend 7 53 6 etc...
文件中存在多个以$开头的关键字标记区块,例如$coord、$intdef。需求是仅删除$coord区块内各行末尾的f字符,处理后的预期输出如下:
$coord -6.81387808414325 5.82189470091282 -1.45477353169903 c 3.12250219010826 1.39239934150351 0.78451413146001 o -4.76572488013335 -1.67551810949494 -1.58797087759328 c -0.15061495158492 -2.18614667480844 -2.60227003662941 c etc... 9.21060449992324 -2.77968508411378 0.71587738888748 h 5.87109372056745 -2.67040600177892 0.54514819243204 h 7.70747476642116 -1.85827163328137 -2.12317155170529 h 3.16053583847830 1.75657003778612 4.21784993053015 h 3.20523873898751 2.06642906155866 6.03962166222879 o 3.84518636016769 0.52341324778083 6.76769535558585 h $intdef # definitions of internal coordinates 1 f 1.0000000000000 stre 6 21 val= 2.05908 2 f 1.0000000000000 stre 6 53 val= 2.07110 3 f 0.0463401612403 bend 53 21 6 val= 1.20720 0.5016372600998 bend 7 21 6 0.4983829790270 bend 7 53 6 etc...
处理规则
- 仅在匹配到
$coord关键字后、遇到下一个$开头关键字前的范围内,执行行尾f字符删除操作 - 其他区块(如
$intdef)内的f字符全部保留 - 除目标末尾
f及分隔它的前置空格外,不得修改任何其他内容
此前尝试用bash实现,已知可以用awk删除最后一列:awk 'NF{NF=-1};1',也可以用sed匹配$coord区块范围:sed -n '/$coord/,/\$/{/$coord/!{/\$/!p}}',但无法将二者组合得到正确结果,需要简便可靠的实现方案。
实现方案
使用awk处理逻辑最清晰,也不会破坏原有文件格式,命令如下:
awk ' /^\$/ { in_coord = ($0 == "$coord") } in_coord && /[[:space:]]+f$/ { sub(/[[:space:]]+f$/, "") } 1' coord > coord_processed
命令逻辑说明
- 第一行规则:匹配所有以
$开头的区块标记行,自动判断当前所处区块:如果是$coord则将状态标记in_coord设为1,遇到其他$开头的区块标记则将标记设为0,自动退出处理范围 - 第二行规则:仅当处于
$coord区块内、且当前行以「一个或多个空白+字符f」结尾时,才替换掉行尾的这部分内容,不会误删行中其他位置的f,也不会改动没有末尾f标记的行 - 第三行的
1表示打印所有行,保证非$coord区块的内容完全原样输出,不会出现内容丢失或格式错乱
如果需要直接修改原文件,可以安装moreutils工具包后使用sponge命令实现原地修改:
awk ' /^\$/ { in_coord = ($0 == "$coord") } in_coord && /[[:space:]]+f$/ { sub(/[[:space:]]+f$/, "") } 1' coord | sponge coord
内容的提问来源于stack exchange,提问作者Programmer
相关产品推荐
相关产品推荐

