SED: Delete X Lines After a Match

Asked by At

I'm trying to delete a range using sed. The range is from a known match and the next 2 lines. Let's say I want to delete all lines that start with Don't and then the 2 lines that follow.

Please note that I am also doing substitutions in a sed command file. As such, I am avoiding using -n and /p in my solution space.

For whatever reason, I want to limit myself to one invocation of sed.

Here's my data (data.txt):

Print Me
Please Output This line
Don't Print Me and 2 more lines
This line is no good
So is this one
We should see this line
And this one, too.

Here's my expected output:

Print Me
Please Output This line
We should see this line
And this one, too.

Here's an attempted go at this:

sed -f delete_dont_plus_2.sed data.txt

With this as delete_dont_plus_2.sed:

/^Don't/,+2d

Here is my result:

sed: 1: delete_dont_plus_2.sed: expected context address

I've also tried these:

/^Don't/,/^Don't/+2d
/^Don't/,{/^Don't/+2}d

Second approach to this question:

Let's say we wanted to make this code a little more robust. Today there are 2 more lines that need to be deleted, but who knows how many lines there will be in the future. Let's say we want to delete up to, but not including We should see this line. In this variant of the question, the results are exactly the same. Again, let's consider a limited BSD sed so we cannot use an expression like /^Don't/,/^We should see this line/-1d.

Thanks!

3 Answers

3
oguzismail On Best Solutions

You're probably using a sed that doesn't support adresses in regexp,+n form. Here is a workaround for this particular case:

/^Don't/{N;N;d;}

It simply reads two more lines into the pattern space when ^Don't is found and deletes them altogether.

But anyways, I think sed is not the right tool for this, you should use instead. E.g:

awk '/^Don\047t/{c=2;next} !(c&&c--)' file

c.f: Printing with sed or awk a line following a matching pattern


wrt

Let's say we want to delete up to, but not including We should see this line. In this variant of the question, the results are exactly the same.

Using sed you'd need to write same RE twice:

/^Don't/,/^We should see this line/{/^We should see this line/!d;}

Using awk you don't even need REs:

awk 'index($0,"Don\047t")==1{d=1} $0=="We should see this line"{d=0} !d' file
1
Cyrus On

With GNU sed:

sed "/^Don't/,+2d" file

Output:

Print Me
Please Output This line
We should see this line
And this one, too.
0
potong On

This might work for you (GNU sed):

sed '/^Don'\''t/{s/.*/X/;h;d};x;/X/!{x;b};s/^/X/;/^XXX/z;x;d' file

This solution keeps a count from the time the regexp is encounterd.

N.B. If one of the two lines following the regexp is also the regexp, the count is restarted.

A solution less GNU specific and programmatic:

sed '/^Don'\''t/{s/.*/X/;h;d};x;/X/!{x;b};s/^/X/;/^XX\{2\}/s/.*//;x;d' file