23

I am trying to remove all instances of a pattern match from a file if it matches a pattern. If there is a match, the (complete) line with the matching pattern and the next line get removed.

The next line always appears after the line with the pattern match, but in addition it appears in other areas of the file.

I am using grep and it is deleting all occurrences of the next line in the file, as expected.

Is there a way I can remove that next line if and only if it is after the line with the pattern match?

2
  • 3
    Please add some example input and output. I think I know what you're trying to do; and it would be easy to do with awk, but I'd like to make sure I'm clear on what you want before I post an answer.
    – HalosGhost
    Aug 26, 2014 at 22:05
  • 2
    Welcome to U&L, please try and include some example data of what you're asking for. It's difficult to parse what you want w/o it, as is evidence in the A'ers you're receiving. They all work slightly different and are making this a bit of a messy Q&A.
    – slm
    Aug 27, 2014 at 1:26

5 Answers 5

27

You can use sed with the N and d commands and a {} block:

sed -e '/pattern here/ { N; d; }'

For every line that matches pattern here, the code in the {} gets executed. N takes the next line into the pattern space as well, and then d deletes the whole thing before moving on to the next line. This works in any POSIX-compatible sed.

8
  • This doesn't appear to work, try using mikeserv's sample data w/ it.
    – slm
    Aug 27, 2014 at 1:10
  • 1
    The main pattern never appears on the last line of the input, per the question. Aug 27, 2014 at 1:31
  • Where did you read that in the main Q? I'm missing that condition.
    – slm
    Aug 27, 2014 at 1:40
  • 1
    "The next line always appears after the line with the pattern match" implies that there must be a following line, and so the main pattern can't ever occur on the last line. Aug 27, 2014 at 1:48
  • We'll wait for the OP, b/c I didn't not read that line at all like that. As I mentioned in steeldriver's A. Oh well, we wait, no biggie.
    – slm
    Aug 27, 2014 at 1:51
5

Most seds do not print the last line if you N on $. But GNU sed will. So if the pattern you want deleted is on the last line and you N it will print. It is sometimes better to keep the buffer full - as in, always holding 2 lines except on the lines you do not want to print. You might do so like:

seq 10 | sed -n 'x;/7/!g;//!p'

That's an example with seq as input. On every line it swaps hold space and pattern space. If the last held line does not match a 7 (in this case) it will overwrite the hold space with the current line. It then checks again that the line it just pulled in - the current line - also does not match the 7, otherwise it will not print it. So on every line it checks the previous and the current line.

1
2
3
4
5
6
9
10

And if your pattern does fall on the last line:

seq 10 | sed -n 'x;/10/!g;//!p' 

1
2
3
4
5
6
7
8
9

Another example, to hopefully demonstrate more clearly what it will and will not print:

sed -n 'x;/match/!g;//!p
' <<\DATA
    match 
    match 
    1not 
    2not 
    3not 
    4not 
    5not 
    match 
    6not 
    match                                                              
DATA

OUTPUT

2not
3not
4not
5not
2
  • 2
    Using the example from this A, this appears to be the only one that handles the edge scenarios properly.
    – slm
    Aug 27, 2014 at 1:12
  • Does not work with -i (in-place): every line will be duplicated.
    – sebix
    Nov 10, 2016 at 20:42
5

An awk solution:

awk '/pattern/    { matched=1; next }
     1 == matched { matched = 0; next }
                  { print }' input-file

Line 1 looks for lines matching the pattern, sets a flag, and skips the line. Line 2 skips a line when the flag is set, but resets the flag. Line 3 prints lines that weren't skipped by one of the other two lines.

You can type this on one line if you like:

awk '/pattern/{matched=1;next} 1==matched{matched=0;next} {print}' input-file

Here's a variation that lets you control how many lines to skip:

awk '/pattern/{skip=3;next} skip>0{--skip;next} {print}' input-file

Set skip to the number of lines that should be skipped (in addition to the matched line).

4
cat sample
Pattern line
next after pattern line
this is another
random line
next after pattern line
has occured without.
now we have
another 
Pattern line
next after pattern line
let us see the output.

Now, I issue the below command.

sed -e '/Pattern \line/,+1d' sample

This is the output I am getting.

this is another
random line
next after pattern line
has occured without.
now we have
another 
let us see the output.

next after pattern line is not removed when it occurs as a separate line not next to the pattern.

As per Michael's comments, the above syntax is GNU extension. You can try something like,

sed -e '/Pattern \line/{N;d}' sample
3
4

How about

sed '/pattern/ {$!N;d;}' file
9
  • This doesn't appear to work, try using mikeserv's sample data w/ it.
    – slm
    Aug 27, 2014 at 1:09
  • However the question says [t]he next line always appears after the line with the pattern match Aug 27, 2014 at 1:18
  • Yeah this Q is poorly written IMO. I've read that statement 10 times and do not understand what it's actually saying. I think he needs to include sample data and clean it up or we should just close it.
    – slm
    Aug 27, 2014 at 1:24
  • How about now (changed N to $!N)? Aug 27, 2014 at 1:26
  • That's consistent with Mike's output now (assuming he's constructed it right) 8-)
    – slm
    Aug 27, 2014 at 1:27

Not the answer you're looking for? Browse other questions tagged or ask your own question.