繁体   English   中英

SED,删除图案之间的线

[英]SED, deleting lines between the patterns

这与使用sed删除模式之间的行(带有模式的行除外)有关。

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

我该怎么做?

主要要认识到的是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.

我喜欢使用它,因为它不涉及跳转标签。 当涉及到某些sed(尤其是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两次传递给awk ,因此awk将处理该文件两次。 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)

要跟踪Wintermute的答案,如果您找到了一个匹配的块,则可以一路删除它,这样就不必将整个文件保留在内存中:

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

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

没有示例输入,因此猜测示例文件和模式/ 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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM