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?

don_crissti
  • 82,805
kenuse
  • 241
  • 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 '14 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 '14 at 01:26

5 Answers5

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.

Michael Homer
  • 76,565
  • This doesn't appear to work, try using mikeserv's sample data w/ it. – slm Aug 27 '14 at 01:10
  • 1
    The main pattern never appears on the last line of the input, per the question. – Michael Homer Aug 27 '14 at 01:31
  • Where did you read that in the main Q? I'm missing that condition. – slm Aug 27 '14 at 01: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. – Michael Homer Aug 27 '14 at 01: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 '14 at 01:51
  • @slm - That is a valid assumption, I think. I usually err on the side of caution with those kind of things, but this is a good answer. It's just, well... there are three of them? Is that weird? – mikeserv Aug 27 '14 at 01:55
  • @mikeserv- LOL, that's why I started testing all these A'ers, since they all appeared to be returning different results when I was reviewing them for you guys. We do have some Q's with like 10 A's and it's usually Q's like these, but they generally all return the same results 8-). – slm Aug 27 '14 at 01:57
  • @MichaelHomer would you give a solution for general case that means for n lines after the match. This will help me on https://tex.stackexchange.com/questions/408155/how-can-i-remove-a-section-from-tex-file-by-its-name – alhelal Dec 31 '17 at 14:05
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
mikeserv
  • 58,310
  • 2
    Using the example from this A, this appears to be the only one that handles the edge scenarios properly. – slm Aug 27 '14 at 01:12
  • Does not work with -i (in-place): every line will be duplicated. – sebix Nov 10 '16 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).

Kenster
  • 3,410
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
Ramesh
  • 39,297
4

How about

sed '/pattern/ {$!N;d;}' file
steeldriver
  • 81,074
  • This doesn't appear to work, try using mikeserv's sample data w/ it. – slm Aug 27 '14 at 01:09
  • However the question says [t]he next line always appears after the line with the pattern match – steeldriver Aug 27 '14 at 01: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 '14 at 01:24
  • How about now (changed N to $!N)? – steeldriver Aug 27 '14 at 01:26
  • That's consistent with Mike's output now (assuming he's constructed it right) 8-) – slm Aug 27 '14 at 01:27
  • Hmm @slm actually it only handles @mikeserv's second edge case I think - still doesn't handle the (possibly not required) case of instances of pattern in adjacent lines. Let's see if OP clarifies. – steeldriver Aug 27 '14 at 01:31
  • Agreed, I was more perplexed by 4 A's seemingly having 4 different solutions to a supposedly single Q 8-). Hopefully he'll come back and fix this. – slm Aug 27 '14 at 01:41
  • Look - steeldriver - it works fine. It's a good answer - I just upvoted. It's best to be careful with n/N - escpecially because different seds do different things with them. GNU sed's info page says it violates POSIX spec with N. Of course, I only really use the one sed so I wouldn't know, really... – mikeserv Aug 27 '14 at 01:58
  • I tried this with +5d but it doesn't work like the other guy's answer did. See: https://askubuntu.com/questions/1195388/how-to-remove-dev-loops/1195398#1195398 for +5d example. – WinEunuuchs2Unix Dec 12 '19 at 01:09