删除最后匹配模式之间的行

时间:2018-04-19 21:54:16

标签: bash perl awk sed

首先,我了解these nice个问题。我的问题有点不同:鉴于下面的文字格式来自file1

Pattern 1
some text to keep
nice text here
Pattern 1
another text to keep
Pattern 1
REMOVE ME
AND ME
ME TOO PLEASE
Pattern 2

如何仅删除上一个Pattern 1Pattern 2之间的文字,包括模式,以便file1现在包含:

Pattern 1
some text to keep
nice text here
Pattern 1
another text to keep

我更喜欢用sed解决方案,但任何其他解决方案(perl,bash,awk)都可以。

4 个答案:

答案 0 :(得分:2)

perl -ne 'if    (/Pattern 1/) { print splice @buff; push @buff, $_ }
          elsif (/Pattern 2/) { @buff = () }
          elsif (@buff)       { push @buff, $_ }
          else                { print }
' -- file

当您看到Pattern 1时,开始将行推入@buff,输出到目前为止累积的所有行。当您看到Pattern 2时,请清除缓冲区。如果缓冲区已经启动,请将任何其他行推送到它,否则打​​印它(在第一个Pattern 1之前或Pattern 2之后的文本。

注意:未指定Pattern 2之前Pattern 1的行为。

答案 1 :(得分:2)

在单独的sed中,我想不出一种简单而优雅的方法。有可能使用write-only code使用sed执行此操作,但我需要一个非常好的理由来编写类似的东西。 : - )

您仍然可以将sed与其他工具结合使用:

$ tac test.txt | sed '/^Pattern 2$/,/^Pattern 1$/d' | tac
Pattern 1
some text to keep
nice text here
Pattern 1
another text to keep

如果您的系统上没有tac,您可以创建一个:

$ alias tac="awk '{L[i++]=\$0} END {for(j=i-1;j>=0;)print L[j--]}'"

或与主题保持一致:

$ alias tac='sed '\''1!G;h;$!d'\'

那就是说,我会在awk中这样做,就像这样:

$ awk '/Pattern 1/{printf "%s",b;b=""} {b=b $0 ORS} /Pattern 2/{b=""} END{printf "%s",b}' text.txt
Pattern 1
some text to keep
nice text here
Pattern 1
another text to keep

或拆分以便于阅读/评论:

awk '
  /Pattern 1/ {          # If we find the start pattern,
    printf "%s",b        # print the buffer (or nothing if it's empty)
    b=""                 # and empty the buffer.
  }
  {                      # Add the current line to a buffer, with the
    b=b $0 ORS           # correct output record separator.
  }
  /Pattern 2/ {          # If we find our close pattern,
    b=""                 # just empty the buffer.
  }
  END {                  # And at the end of the file,
    printf "%s",b        # print the buffer if we have one.
  }' test.txt

这与hek2mgl的解决方案大致相同,但更合理地命令并使用ORS。 : - )

请注意,仅当Pattern 2在文件中仅存在一次时,这两种解决方案才能正常运行。如果你有多个块,即包含开始和结束模式,你需要更加努力地工作。如果是这种情况,请在您的问题中提供更多详细信息。

答案 2 :(得分:1)

使用awk:

awk '
# On pattern 1 and when the buffer is not empty, flush the buffer
/Pattern 1/ && b!="" { printf "%s", b; b="" }

# Append the current line and a newline to the buffer
{ b=b""$0"\n" }

# Clean the buffer on pattern 2
/Pattern 2/ { b="" }' file

答案 3 :(得分:1)

这可能适合你(GNU sed):

sed '/Pattern 1/,${//{x;//p;x;h};//!H;$!d;x;s/.*Pattern 2[^\n]*\n\?//;/^$/d}' file

这里的一般想法是收集以Pattern 1开头的行,然后在遇到以Pattern 1开头的另一行时刷新这些行,或者在文件结尾处删除Pattern 1之间的行。 1}}和Pattern 2并打印剩下的内容。

关注包含Pattern 1的第一行和文件结尾之间的行,正常打印所有其他行。如果一行包含Pattern 1,则交换到保留空间,如果这些行也包含相同的正则表达式,则打印这些行,然后替换保留空间中的当前行。如果当前行不包含正则表达式,则将其附加到保留空间,如果不是文件结尾,则将其删除。在文件结尾处,交换到保留空间并删除任何行,包括包含Pattern 2的行,并打印剩余的行。

N.B。当您的示例中包含Pattern 2的行是文件的最后一行时,会出现一个棘手的情况。由于sed使用换行来分隔线条,因此在将线条放入图案空间并在打印之前附加它们之前将其删除。如果模式/保持空间为空,则sed将附加换行符,在这种情况下会添加虚假换行符。解决方案是删除Pattern 1Pattern 2之间的任何行,包括包含Pattern 2的行后面的任何换行符。如果还有其他行将按正常方式打印,但是如果没有后续行,则保留空间现在将为空,因为它之前必须包含一些内容,因为它现在是空的,可以安全地删除它。