nowox
nowox

Reputation: 29178

grep that match around the first match

I would like to grep a specific word 'foo' inside specific files, then get the N lines around my match and show only the blocks that contain a second grep.

I found this but it doesn't really work...

find . | grep -E '.*?\.(c|asm|mac|inc)$' | \
xargs grep --color -C3 -rie 'foo' | \
xargs -n1 --delimiter='--' | grep --color -l 'bar'

For instance I have the file 'a':

a
b
c
d
bar
f
foo
g
h
i
j
bar
l

The file b:

a
bar
c
d
e
foo
g
h
i
j
k

I expect this for grep -c2 on both files because bar is contained in the -c2 range of foo. I do not get any match for ./bar because bar is not in the range -c2 of foo...

--
./foo-    bar
./foo-    f
./foo-    **foo**
./foo-    g
./foo-    h
--

Any ideas?

Upvotes: 0

Views: 136

Answers (3)

Sintrinsic
Sintrinsic

Reputation: 86

You could do this pretty simply with a "while read line" loop:

find -regextype posix-extended -regex "./file[a-z]" | while read line; do grep -nHC2 "foo" $line | grep --color bar; done

Output:

./filea-5-bar
./filec-46-... host pwns.me [94.23.120.252]: 451 4.7.1 Local bar configuration error ...

In this example, I created the following files:
filea - your example a
fileb - your example b
filec - some random exim log output with foo and bar tossed in 2 lines apart
filed - the same exim log output, but with foo and bar tossed in 3 lines apart

You could also pipe the output after done, to alter the format:

; done | sed 's/-([0-9]{1,6})-/: line: \1 ::: /'

Formatted output
./filea: line: 5 ::: bar
./filec: line: 46 ::: ... host pwns.me [94.23.120.252]: 451 4.7.1 Local bar configuration error ...

Upvotes: 1

Mark Setchell
Mark Setchell

Reputation: 208107

I think I only understand the first line of your question and this does what I think you mean!

#!/bin/bash
N=2
pattern1=a
pattern2=z
matchinglines=$(awk -v p="$pattern1" '$0~p{print NR}' file)    # Generate array of matching line numbers
for x in ${matchinglines[@]}
do
   ((start=x-N))
   [[ $start -lt 1 ]] && start=1   # Avoid passing negative line nmumbers to sed
   ((end=x+N))
   echo DEBUG: Checking block between lines $start and $end
   sed -ne "${start},${end}p" file | grep -q "$pattern2"
   [[ $? -eq 0 ]] && sed -ne "${start},${end}p" file
done

You need to set pattern1 and pattern2 at the start of the script. It basically does some awk to build an array of the line numbers that match your first pattern. Then it loops through the array and sets the start and end range to +/-N either side of each matching line number. It then uses sed to extraact that block and passes it through grep to see if it contains pattern2 printing it if it does. It may not be the most efficient, but it is easy enough to understand and maintain.

It assumes your file is called file

Upvotes: 0

Ehwas
Ehwas

Reputation: 11

pipe it twice

 grep "[^foo\n]" | grep "\n{ntimes}foo\n{ntimes}"

Upvotes: 0

Related Questions