删除相对于包含模式的行的前n1行和后n2行

sed -e '/XXXX/,+4d' fv.out 

我必须在一个文件中find一个特定的模式,同时删除上面5行和下面4行。 我发现上面的这行删除了包含模式的行和它下面的四行。

 sed -e '/XXXX/,~5d' fv.out 

在sed手册中给出〜表示模式后面的行。 但是当我尝试它时,它是被删除的模式之后的行。

那么,如何同时删除含有该模式的行的上面5行和下面的4行呢?

一种使用sed方式,假设模式不够接近:

script.sed内容:

 ## If line doesn't match the pattern... /pattern/ ! { ## Append line to 'hold space'. H ## Copy content of 'hold space' to 'pattern space' to work with it. g ## If there are more than 5 lines saved, print and remove the first ## one. It's like a FIFO. /\(\n[^\n]*\)\{6\}/ { ## Delete the first '\n' automatically added by previous 'H' command. s/^\n// ## Print until first '\n'. P ## Delete data printed just before. s/[^\n]*// ## Save updated content to 'hold space'. h } ### Added to fix an error pointed out by potong in comments. ### ======================================================= ## If last line, print lines left in 'hold space'. $ { xs/^\n// p } ### ======================================================= ## Read next line. b } ## If line matches the pattern... /pattern/ { ## Remove all content of 'hold space'. It has the five previous ## lines, which won't be printed. xs/^.*$// x ## Read next four lines and append them to 'pattern space'. N ; N ; N ; N ## Delete all. s/^.*$// } 

运行如下:

 sed -nf script.sed infile 

这个想法是读5行而不打印它们。 如果找到图案,请删除未打印的线条和下面四行。 如果没有找到图案,请记住当前行并打印第一条未打印的行。 最后,打印什么是未打印的。

 sed -n -e '/XXXX/,+4{x;s/.*//;x;d}' -e '1,5H' -e '6,${H;g;s/\n//;P;s/[^\n]*//;h}' -e '${g;s/\n//;p;d}' fv.out 

当然,这只有在文件中出现一次你的模式时才有效。 如果你有很多,你需要在找到你的模式后再读5行,如果你再次在这些行中有你的模式,就会变得复杂。 在这种情况下,我认为sed不是正确的工具。

使用awk的解决方案:

 awk '$0 ~ "XXXX" { lines2del = 5; nlines = 0; } nlines == 5 { print lines[NR%5]; nlines-- } lines2del == 0 { lines[NR%5] = $0; nlines++ } lines2del > 0 { lines2del-- } END { while (nlines-- > 0) { print lines[(NR - nlines) % 5] } }' fv.out 

更新:

这是脚本解释:

  • 我记得使用旋转索引(NR%5; NR是记录号;在这种情况下是行)的阵列lines的最后5行。
  • 如果我在当前行( $0 ~ "XXXX ; $0是当前记录:在这种情况下是行;并且是扩展正则表达式匹配运算符)中找到该模式,则重置读取的行数并注意到删除5行(包括当前行)。
  • 如果我已经读了5行,我打印当前行。
  • 如果我没有行删除(这也是如果我读了5行,我把当前行放入缓冲区,并增加行数。注意行数减少,然后递增,如果一行是打印。
  • 如果需要删除行,我不会打印任何内容并减少要删除的行数。
  • 在脚本的最后,我打印出数组中的所有行。

我的原始版本的脚本是以下,但我最终优化到以上版本:

 awk '$0 ~ "XXXX" { lines2del = 5; nlines = 0; } lines2del == 0 && nlines == 5 { print lines[NR%5]; lines[NR%5] } lines2del == 0 && nlines < 5 { lines[NR%5] = $0; nlines++ } lines2del > 0 { lines2del-- } END { while (nlines-- > 0) { print lines[(NR - nlines) % 5] } }' fv.out 

awk是一个伟大的工具! 我强烈建议您在网上找到一个教程,并阅读它。 一个重要的事情: awk扩展正则表达式ERE )一起工作。 它们的语法与sed使用的标准正则表达式RE )有些不同,但是可以用ERE完成所有可以用RE完成的语法。

这可能适合你:

 sed 'H;$!d;g;s/\([^\n]*\n\)\{5\}[^\n]*PATTERN\([^\n]*\n\)\{5\}//g;s/.//' file 

或这个:

 awk --posix -vORS='' -vRS='([^\n]*\n){5}[^\n]*PATTERN([^\n]*\n){5}' 1 file 

更高效的sed解决方案:

 sed ':a;/PATTERN/,+4d;/\([^\n]*\n\)\{5\}/{P;D};$q;N;ba' file 

如果你很乐意将结果输出到一个文件而不是标准输出, vim可以非常有效地完成:

 vim -c 'g/pattern/-5,+4d' -c 'w! outfile|q!' infile 

要么

 vim -c 'g/pattern/-5,+4d' -c 'x' infile 

在原地编辑文件。