Remove line containing certain string and the following line

I use this

cat foo.txt | sed '/bar/d'

to remove lines containing the string bar in the file.

I would like however to remove those lines and the line directly after it. Preferably in sed, awk or other tool that’s available in MinGW32.

It’s a kind of reverse of what I can get in grep with -A and -B to print matching lines as well as lines before/after the matched line.

Is there any easy way to achieve it?

Asked By: jakub.g

||

I am not fluent in sed, but it is easy to do so in awk:

awk '/bar/{getline;next} 1' foo.txt 

The awk script reads: for a line containing bar, get the next line (getline), then skip all subsequent processing (next). The 1 pattern at the end prints the remaining lines.

Update

As pointed out in the comment, the above solution did not work with consecutive bar. Here is a revised solution, which takes it into consideration:

awk '/bar/ {while (/bar/ && getline>0) ; next} 1' foo.txt 

We now keep reading to skip all the /bar/ lines.

Answered By: Hai Vu

You will want to make use of sed’s scripting capabilities to accomplish this.

$ sed -e '/bar/ { 
 $!N
 d
 }' sample1.txt

Sample data:

$ cat sample1.txt 
foo
bar
biz
baz
buz

The “N” command appends the next line of input into the pattern space. This combined with the line from the pattern match (/bar/) will be the lines that you wish to delete. You can then delete normally with the “d” command.

Answered By: Michael Rollins

If bar may occur on consecutive lines, you could do:

awk '/bar/{n=2}; n {n--; next}; 1' < infile > outfile

which can be adapted to delete more than 2 lines by changing the 2 above with the number of lines to delete including the matching one.

If not, it’s easily done in sed with @MichaelRollins’ solution or:

sed '/bar/,/^/d' < infile > outfile
Answered By: Stéphane Chazelas

If you have GNU sed (so non-embedded Linux or Cygwin):

sed '/bar/,+1 d'

If you have bar on two consecutive lines, this will delete the second line without analyzing it. For example, if you have a 3-line file bar/bar/foo, the foo line will stay.

If any line immediately following a match should be removed then your sed program will have to consider consecutive matches. In other words, if you remove a line following a match which also matches, then probably you should remove the line following that as well.

It is implemented simply enough – but you have to look-behind a little.

printf %s\n     0 match 2 match match 
                 5 6 match match match 
                 10 11 12 match 14 15  |
sed -ne'x;/match/!{g;//!p;}'

0
6
11
12
15

It works by swapping hold and pattern spaces for each line read in – so the last line can be compared to the current each time. So when sed reads a line it exchanges the contents of its buffers – and the previous line is then the contents of its edit buffer, while the current line is put in hold space.

So sed checks the previous line for a match to match, and if its ! not found the two expressions in the { function } are run. sed will get the hold space by overwriting the pattern space – which means the current line is then in both the hold and pattern spaces – and then it will // check it for a match to its most recently compiled regular expression – match – and if it does not match it is printed.

This means a line is only printed if it does not match and the immediately previous line does not match. It also foregoes any unnecessary swaps for sequences of matches.

If you wanted a version that could drop an arbitrary number of lines occurring after a match it would need a little more work:

printf %s\n    1 2 3 4 match  
                match match 8  
                9 10 11 12 13  
                14 match match 
                17 18 19 20 21 |
sed -net -e'/match/{h;n;//h;//!H;G;s/n/&/5;D;}' -ep

…replace the 5 with the number of lines (including the matched line) that you would like to remove…


1
2
3
4
12
13
14
21
Answered By: mikeserv
Categories: Answers Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.