SnazzyBootMan
SnazzyBootMan

Reputation: 759

sed remove first pattern match only

I would like to match a set of data between two patterns and remove this data and the start/end patterns but only for the first occurrence of the pattern.

So if this is the test data:

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

This will quite happy remove all the pattern matches and the lines in between but I only want to remove the first pattern match and the lines in between:

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

Output:

TESTLINE1
TESTLINE2
TESTLINE3

Required output:

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

Any sed ideas?

Upvotes: 6

Views: 2838

Answers (6)

mop
mop

Reputation: 433

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

Upvotes: 2

potong
potong

Reputation: 58568

This might work for you (GNU sed):

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

This uses the hold space as a switch. Check the file for the desired range of lines. If encountered and the hold space is not empty, the first range has already been deleted so bail out and print as normal. If not, set the switch on the last pattern match and delete all lines within the range.

Upvotes: 0

PatJ
PatJ

Reputation: 6140

You can do this with that (quite ugly I admit) sed code:

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

Let's look at it more closely:

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

Upvotes: 0

Wintermute
Wintermute

Reputation: 44063

It's a bit incredible-machiney, but this works:

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

as follows:

/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
}

The xs in that code are largely to ensure that the counter ends up back in the hold buffer when the whole thing is over. The // bit works because // repeats the last attempted regex, which is the start pattern of the range for its first line and the end pattern for the others.

Upvotes: 4

Ed Morton
Ed Morton

Reputation: 204558

Just use awk (the cat -n is just so you can see which line numbers are being printed):

$ 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

Set the test on c to be the occurrence number of whatever block you want to skip:

$ 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

Upvotes: 3

randomusername
randomusername

Reputation: 8105

Use

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

The q command causes sed to quit.

Upvotes: -1

Related Questions