如何在Bash命令行用Perl/Sed移除换行,保留特定字符结尾行并替换为空格?
我来帮你解决这个换行符处理的问题!你要的是把那些不以句号、冒号结尾的行的换行符替换成空格,同时保留符合规则的行的换行,还要避免之前代码误删行尾字符的坑对吧?下面分别给你Sed和Perl的可靠解决方案:
Sed 解决方案
Sed的核心思路是循环合并不符合结尾规则的行,只替换换行符本身,不会碰行尾的有效字符。
基础命令(输出到终端)
sed -e ':a' -e '/[.:]$/!{N;s/\n/ /;ba}' input.txt
命令拆解
:a:定义一个名为a的标签,用来实现循环逻辑/[.:]$/!:判断当前行不是以.或:结尾时,执行后续操作N:把下一行内容追加到当前处理的模式空间里(两行合并,中间保留换行符)s/\n/ /:把模式空间里的换行符替换成空格ba:跳回到标签a,继续检查合并后的行是否符合结尾规则(如果还是不符合,就继续合并下一行)
原地修改文件(In-place编辑)
如果需要直接修改原文件,GNU Sed(Linux系统)可以加-i参数:
sed -i -e ':a' -e '/[.:]$/!{N;s/\n/ /;ba}' input.txt
BSD Sed(比如MacOS)需要给-i加空参数:
sed -i '' -e ':a' -e '/[.:]$/!{N;s/\n/ /;ba}' input.txt
Perl 解决方案
Perl处理跨行文本更灵活,这里提供两种写法,分别适合不同场景:
写法1:段落模式(简洁高效,适合普通文件)
一次性读入文件,用正则精准替换不符合条件的换行符:
perl -00 -pe 's/(?<![:.])\n/ /g' input.txt
命令拆解
-00:启用段落模式,Perl会把整个文件当作一个连续的字符串处理(?<![:.])\n:用负向后瞻正则,只匹配前面不是.或:的换行符s/.../ /g:全局替换所有符合条件的换行符为空格
写法2:逐行缓存(适合超大文件,低内存占用)
如果文件特别大,一次性读入内存压力大,可以用逐行读取并缓存的方式:
perl -ne 'chomp; if (/[.:]$/) { print "$_\n"; $buf = ""; } else { $buf .= "$_ "; } END { print $buf }' input.txt
命令拆解
-ne:逐行读取输入文件,执行代码块chomp:去掉当前行的默认换行符- 条件判断:如果当前行以
.或:结尾,就打印该行加换行,同时清空缓存;否则把当前行加空格追加到缓存 END块:处理最后一行(如果最后一行不符合结尾规则,把缓存里的内容打印出来)
原地修改文件(In-place编辑)
两种写法都可以用-i参数实现原地修改:
# 段落模式原地修改 perl -i -00 -pe 's/(?<![:.])\n/ /g' input.txt # 逐行缓存原地修改 perl -i -ne 'chomp; if (/[.:]$/) { print "$_\n"; $buf = ""; } else { $buf .= "$_ "; } END { print $buf }' input.txt
为什么之前的代码会删行尾字符?
你之前的代码大概率用了类似s/.$/ /的正则,这个表达式会匹配行的最后一个字符(不管是什么),然后替换成空格,自然就把行尾的有效字符删掉了。上面的方案都只针对换行符做替换,不会触碰行尾的.或:,完美解决这个问题。
内容的提问来源于stack exchange,提问作者Gabz
相关产品推荐
相关产品推荐

