sed - 使用 sed 删除模式前的空行
问题描述
语境
例如我有这个测试文件foo.py
:
#!/usr/bin/env python3
'''foo'''
# comment
import ...
# [END import]
import ...
# [END import]
import ...
# [END import]
# [END import]
import even...
# [END import]
# [END import]
import odd...
# [END import]
# [END import]
预期的
我想删除之前的空行# [END import
#!/usr/bin/env python3
'''foo'''
# comment
import ...
# [END import]
import ...
# [END import]
import ...
# [END import]
# [END import]
import even...
# [END import]
# [END import]
import odd...
# [END import]
# [END import]
有人可以给我一个使用sed的工作版本和/或解释为什么以下内容不起作用
测试 0
sed '$!N;s/^\n\(# \[END\)/\1/g' foo.py
观测到的
#!/usr/bin/env python3
'''foo'''
# comment
import ...
# [END import]
import ...
# [END import]
import ...
# [END import]
# [END import]
import even...
# [END import]
# [END import]
import odd...
# [END import]
# [END import]
这里只有“偶数”行发生了变化,因为在这里我们一次使用“消耗”两行N;
而不回来......
测试 1
sed ':r;$!{N;br};s/^\n\(# \[END\)/\1/g' foo.py
观测到的
没有任何变化,在这里我不明白为什么它不起作用(即为什么模式不匹配)......
测试 2
没有^
锚。
sed ':r;$!{N;br};s/\n\(# \[END\)/\1/g' foo.py
观测到的
#!/usr/bin/env python3
'''foo'''
# comment
import ...
# [END import]
import ...
# [END import]
import ...# [END import]# [END import]
import even...
# [END import]
# [END import]
import odd...
# [END import]
# [END import]
注意#[END
命令预期的同一行上的双精度,但不是预期的结果。
测试 3
sed ':r;$!{N;br};s/\n\(\n# \[END\)/\1/g' foo.py
观测到的
按预期工作,但我不知道为什么它可以匹配,\n\n
即两个连续的回报
解决方案
您需要为测试 1 添加标志,m
以便锚点将匹配每一行的开始和结束位置,否则它们将匹配整个字符串的开始/结束。这假设您的实现支持标志,就像这样做一样。^
$
m
GNU sed
sed ':r;$!{N;br};s/^\n\(# \[END\)/\1/mg'
测试 3 有效,因为在空行之前有一个换行符作为前一行的一部分。下面的示例可能会帮助您更好地可视化它:
$ printf 'a\nb\nc\n'
a
b
c
$ printf 'a\nb\n\nc\n'
a
b
c
使用 perl:
perl -0777 -pe 's/\n\K\n(?=# \[END)//g'
-0777
将整个输入作为单个字符串啜饮\n\K\n(?=# \[END)
将匹配换行符,前提是该换行符前后# \[END
有换行符
,的另一种选择GNU sed
不需要一次读取整个文件。
sed '/^$/{N; s/\n\(# \[END\)/\1/; P; D}'
/^$/
将匹配一个空行N
将下一行添加到模式空间s/\n\(# \[END\)/\1/
如果需要的正则表达式匹配,则删除换行符
P
并且D
在这里至关重要,所以我将引用手册:
P打印到第一个换行符之前的模式空间部分。
D如果模式空间不包含换行符,则开始一个正常的新循环,就像发出了 d 命令一样。否则,删除模式空间中的文本直到第一个换行符,并使用生成的模式空间重新启动循环,而不读取新的输入行。