SED,删除模式之间的线条

时间:2015-06-27 18:52:07

标签: regex bash sed

这是关于使用sed删除带有图案的线条之间的线条。

如果第二种模式经常出现两次或更多次,我希望删除这些行直到最后一次出现第二种模式。

我该怎么做?

3 个答案:

答案 0 :(得分:1)

要实现的主要事情是sed在单独的行上运行,而不是在整个文件上运行,这意味着如果没有特殊处理,它就无法从正则表达式获得多行匹配。要立即对整个文件进行操作,首先必须将整个文件读入内存。有很多方法可以做到这一点;其中一个是

sed '1h; 1!H; $!d; x; s/regex/replacement/' filename

其工作原理如下:

1h   # When processing the first line, copy it to the hold buffer.
1!H  # When processing a line that's not the first, append it to the hold buffer.
$!d  # When processing a line that's not the last, stop working here.
x    # If we get here, we just appended the last line to the hold buffer, so
     # swap hold buffer and pattern space. Now the whole file is in the pattern
     # space, where we can apply regexes to it.

我喜欢使用这个,因为它不涉及跳转标签。一些seds(特别是BSD sed,与* BSD和MacOS X一起提供)在涉及这些时候有点小问题。

所以,剩下的就是制定多行正则表达式。由于您没有指定分隔符模式,因此我假设您要删除包含START的第一行和包含END的最后一行之间的行。这可以通过

来完成
sed '1h; 1!H; $!d; x; s/\(START[^\n]*\).*\(\n[^\n]*END\)/\1\2/' filename

正则表达式不包含任何壮观的东西;主要是你必须小心在正确的位置使用[^\n],以避免超出行尾的贪婪匹配。

请注意,只有文件足够小才能完全读入内存时,此功能才有效。如果不是这种情况,我的建议是用awk对文件进行两次传递:

awk 'NR == FNR && /START/ && !start { start = NR } NR == FNR && /END/ { end = NR } NR != FNR && (FNR <= start || FNR >= end)' filename filename

此操作如下:由于filename两次传递给awkawk将处理文件两次。 NR是整个记录(默认行),FNR到目前为止从当前文件读取的记录数。在文件的第一次传递中,NRFNR是相同的,之后他们不会。所以:

# If this is the first pass over the file, the line matches the start pattern,
# and the start marker hasn't been set yet, set the start marker
NR == FNR && /START/ && !start { start = NR }

# If this is the first pass over the file and the line matches the end line,
# set the end marker to the current line (this means that the end marker will
# always identify the last occurrence of the end pattern that was seen so far)
NR == FNR && /END/             { end   = NR }

# In the second pass, print those lines whose number is less than or equal to
# the start marker or greater than or equal to the end marker.
NR != FNR && (FNR <= start || FNR >= end)

答案 1 :(得分:1)

要跟进Wintermute's答案,如果您找到了匹配的块,您可以在此过程中删除它,这样您就不必保留整个内存中的文件:

sed '/^START$/{:a;N;/.*\nEND$/d;ba}'

(对不起,本来会回复Wintermute的答案,但显然我仍然需要50点声望才能获得该特权)

答案 2 :(得分:0)

没有示例输入,所以猜测一个示例文件和patterns / line3 /和/ line6 /.

line1 #keep - up to 1st pattern line3 - including
line2 #keep
line3 #keep
line4 #delete up to last occurence of line6
line5
line6a
line7
line6b
line8 #delete
line6c #keep - the last line6
line9  #keep
line10 #keep

没有任何黑暗的voo-doo,但效率低下的方法可能是:

(sed -n '1,/line3/p' file; tail -r file | sed -n '1,/line6/p' | tail -r) > file2

file2将包含:

line1
line2
line3
line6c
line9
line10

说明:

sed -n '1,/line3/p' file; # prints line 1 up to pattern (included)

tail -r file | sed -n '1,/line6/p' | tail -r
#reverse the file
#print the lines up to pattern2
#reverse the result