I want to filter out several lines before and after a matching line in a file.

This will remove the line that I don't want:

$ grep -v "line that i don't want" 

And this will print the 2 lines before and after the line I don't want:

$ grep -C 2 "line that i don't want" 

But when I combine them it does not filter out the 2 lines before and after the line I don't want:

# does not remove 2 lines before and after the line I don't want: $ grep -v -C 2 "line that i don't want" 

How do I filter out not just the line I don't want, but also the lines before and after it? I'm guessing sed would be better for this...

Edit: I know this could be done in a few lines of awk/Perl/Python/Ruby/etc, but I want to know if there is a succinct one-liner I could run from the command line.

0

6 Answers

If the lines are all unique you could grep the lines you want to remove into a file, and then use that file to remove the lines from the original, e.g.

grep -C 2 "line I don't want" < A.txt > B.txt grep -f B.txt A.txt 
2

Give this a try:

sed 'h;:b;$b;N;N;/PATTERN/{N;d};$b;P;D' inputfile 

You can vary the number of N commands before the pattern to affect the number of lines to delete.

You could programmatically build a string containing the number of N commands:

C=2 # corresponds to grep -C N=N for ((i = 0; i < C - 1; i++)); do N=$N";N"; done sed "h;:b;\$b;$N;/PATTERN/{N;d};\$b;P;D" inputfile 
4
awk 'BEGIN{n=2}{a[++i]=$0} /dont/{ for(j=1;j<=i-(n+1);j++)print a[j]; for(o=1;o<=n;o++)getline; delete a} END{for(i in a)print a[i]} ' file 
3

I solved it with two sequential grep, actually. It seems way more straightforward to me.

grep -C "match" yourfile | grep -v -f - yourfile 
1

I think @fxm27 has an excellent, bash-y answer.

I would add that you could solve this another way by using egrep if you knew in advance the patterns of the subsequent lines.

command | egrep -v "words|from|lines|you|dont|want" 

That will do an "inclusive OR", meaning that a line that matches any of those will be excluded.

2019 Solution

This is a simple solution, found elsewhere:

grep --invert-match "test*" 

Selects all not matching "test*". Super useful and easy to remember!

(Edit)

This doesn't completely answer the original question and returns the entire set of lines not matching.

1

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy