sed仅删除第一个模式匹配

时间:2015-03-05 22:49:37

标签: sed

我想在两种模式之间匹配一组数据,并删除此数据和开始/结束模式,但仅限于第一次出现模式。

所以如果这是测试数据:

PATTERNSTART
LINE1
LINE2
LINE3
PATTERNEND
PATTERNSTART
LINE1
LINE2
LINE3
PATTERNEND
TESTLINE1
TESTLINE2
TESTLINE3
PATTERNSTART
LINE1
LINE2
LINE3
PATTERNEND

这将很高兴删除所有模式匹配和中间的行,但我只想删除第一个模式匹配和中间的行:

sed '/PATTERNSTART/,/PATTERNEND/d' testsed.txt

输出:

TESTLINE1
TESTLINE2
TESTLINE3

必需的输出:

PATTERNSTART
LINE1
LINE2
LINE3
PATTERNEND
TESTLINE1
TESTLINE2
TESTLINE3
PATTERNSTART
LINE1
LINE2
LINE3
PATTERNEND

任何sed想法?

6 个答案:

答案 0 :(得分:3)

它有点令人难以置信,但是这很有效:

sed '/PATTERNSTART/,/PATTERNEND/ { // { x; s/$/./; x; }; x; /.../! { x; d; }; x; }' filename

如下:

/PATTERNSTART/,/PATTERNEND/ {   # in the pattern range
  // {                          # in the first and last line:
    x
    s/$/./                      # increment a counter in the hold buffer by
                                # appending a character to it. The counter is
                                # the number of characters in the hold buffer.
    x
  }
  x                             # for all lines in the range: inspect the
                                # counter
  /.../! {                      # if it is not three or more (the counter
                                # becomes three with the start line of the
                                # second matching range)
    x
    d                           # delete the line
  }
  x
}

该代码中的x主要是为了确保当整个事情结束时计数器最终返回到保持缓冲区。 //位有效,因为//重复上一次尝试的正则表达式,这是第一行的范围的起始模式和其他的结束模式。

答案 1 :(得分:3)

只需使用awk(cat -n只是为了让您可以看到正在打印的行号):

$ cat -n file | awk '/PATTERNSTART/{f=1;++c} !(f && c==1); /PATTERNEND/{f=0}'
     6  PATTERNSTART
     7  LINE1
     8  LINE2
     9  LINE3
    10  PATTERNEND
    11  TESTLINE1
    12  TESTLINE2
    13  TESTLINE3
    14  PATTERNSTART
    15  LINE1
    16  LINE2
    17  LINE3
    18  PATTERNEND

c上的测试设置为您要跳过的任何块的出现次数:

$ cat -n file | awk '/PATTERNSTART/{f=1;++c} !(f && c==2); /PATTERNEND/{f=0}'
     1  PATTERNSTART
     2  LINE1
     3  LINE2
     4  LINE3
     5  PATTERNEND
    11  TESTLINE1
    12  TESTLINE2
    13  TESTLINE3
    14  PATTERNSTART
    15  LINE1
    16  LINE2
    17  LINE3
    18  PATTERNEND

答案 2 :(得分:1)

sed '/PATTERNSTART/,/PATTERNEND/{0,/PATTERNEND/d}' file

答案 3 :(得分:0)

你可以这样做(我承认非常难看)sed代码:

sed -e '/PATTERNSTART/,/PATTERNEND/{ /PATTERNEND/b after; d; :after; N; s/^.*\n//; :loop; n; b loop; }' testsed.txt

让我们更仔细地看一下:

sed -e '/PATTERNSTART/,/PATTERNEND/{

 /PATTERNEND/b after; # if we're at the end of match, go to the hack
 d;                   # if not, delete the line and start a new cycle

 :after;              # Begin "end of part to delete"
 N;                   # get the next line...
 s/^.*\n//;           # ...and forget about this one

                      # We now only have to print everything:
 :loop; n; b loop;

                      # And you sir, have your code!
}' testsed.txt

答案 4 :(得分:0)

这可能适合你(GNU sed):

sed '/PATTERNSTART/,/PATTERNEND/{x;/./{x;b};x;/PATTERNEND/h;d}' file

这使用保持空间作为开关。检查文件中是否有所需的行范围。如果遇到并且保持空间不为空,则第一个范围已被删除,因此挽救并正常打印。如果没有,请将开关设置为最后一个模式匹配并删除该范围内的所有行。

答案 5 :(得分:-1)

使用

sed -e '/PATTERNSTART/,/PATTERNEND/d' -e '/PATTERNEND/q' some_file.txt

q命令导致sed退出。