text-processing – How to Delete Nth Line from Each Line Matching a Pattern

text processing

I have several files like file1, file2 … etc in the same directory and each file may contain several lines matching PATTERN.
I would like to delete the Nth line from each line matching PATTERN e.g. with N = 3 and file1 content like

1 no match
2 PATTERN
3 same PATTERN
4 no match here
5 no match here either
6 another PATTERN
7 again, no match
8 no
9 last line

the expected output is

1 no match
2 PATTERN
3 same PATTERN
4 no match here
7 again, no match
8 no

Editing the files in-place is a bonus, not a requirement (though there's at least one gnu tool that I know of that could edit them all in one go…)


A similar question was asked here however that is a particular case (there's just a single line matching pattern in each file and the solutions there would only work with multiple lines matching pattern if they're separated by at least N+1 non-matching lines).

Best Answer

You could use awk for this I believe like so:

awk -vN=3 '/PATTERN/ {skips[FNR+N]=1;} {if(!(FNR in skips)) print;}' <file>

so each time we hit PATTERN we'll record the line that is N away from here, and only print those lines we have not marked for skipping.

with gawk you could use -i inplace as well to do it in place

As you noted, that wouldn't handle multiple files. Of course, you could wrap with a for loop to iterate over all the files, but if there aren't enough to make the commandline too long you could also do it like so:

 awk -vN=3 '{if(FNR==1) split("", skips, ":");} /PATTERN/ {skips[FNR+N]=1;} {if(!(FNR in skips)) print;}' *

where we reset skips to an empty array each time FNR hits 1, so the start of each file.
With gnu awk you could write it as:

gawk -i inplace 'FNR==1{delete nr};/PATTERN/{nr[FNR+3]++};!(FNR in nr)' file*
Related Question