首页 > 解决方案 > 使用 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即两个连续的回报

标签: sed

解决方案


您需要为测试 1 添加标志,m以便锚点将匹配每一行的开始和结束位置,否则它们将匹配整个字符串的开始/结束。这假设您的实现支持标志,就像这样做一样。^$mGNU 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 命令一样。否则,删除模式空间中的文本直到第一个换行符,并使用生成的模式空间重新启动循环,而不读取新的输入行。


推荐阅读