M HYPE SPLASH
// news

find -exec grep in series with an inverted grep, find nested grep

By Emma Terry

I have the following three files

**file_01.sh**
GOOD_word
bla bla BAD_word
miao GOOD_word bau
BAD_word
GOOD_word foo
**file_02.sh**
GOOD_word yes
GOOD_word bla bla BAD_word
GOOD_word bla
BAD_word 
**file_03.sh**
just BAD_word
and bla bla 

I would like to have the following result:

**“o_file.txt”:**
./i_dir/file_01.sh:1:GOOD_word
./i_dir/file_01.sh:3:miao GOOD_word bau
./i_dir/file_01.sh:5:GOOD_word foo
./i_dir/file_02.sh:1:GOOD_word yes
./i_dir/file_02.sh:3:GOOD_word bla 

This is what I try so far, but does not work.

find ./dir -type f -exec grep -E -i -H "GOOD_word" {} \; | grep -v “BAD_word” {} \; >> o_file.txt;
0

2 Answers

You do not need find! ... grep has an option for that:

-r, --recursiveRead all files under each directory, recursively, following symbolic links only if they are on the command line. Note that if no file operand is given, grep searches the working directory. This is equivalent to the -d recurse option.

or

-R, --dereference-recursiveRead all files under each directory, recursively. Follow all symbolic links, unlike -r.

More options are in man grep


You can run the following from within the directory containing the three files.

To exclude only the word BAD_word:

sed 's/BAD_word//' <(grep -rwn "GOOD_word" .)

or to exclude the whole lines containing the word BAD_word:

grep -wv "BAD_word" <(grep -rwn "GOOD_word" .)
0

You are close - you just need to add -n for the line numbers and remove the second {} so that the grep -v operates on the output of the first grep, rather than on the found files:

$ find ./i_dir -type f -exec grep -E -i -H -n "GOOD_word" {} \; | grep -v "BAD_word"
./i_dir/file_01.sh:1:GOOD_word
./i_dir/file_01.sh:3:miao GOOD_word bau
./i_dir/file_01.sh:5:GOOD_word foo
./i_dir/file_02.sh:1:GOOD_word yes
./i_dir/file_02.sh:3:GOOD_word bla

I'm assuming that you need -E because your "real" GOOD_word is an extended regular expression - if that's not the case, you may omit it (and possibly also the -i flag).

Alternatively, you could do something like this, using bash globstar for the directory descent and awk for the logic:

shopt -s globstar
awk '/GOOD_word/ && !/BAD_word/{print FILENAME,FNR,$0}' OFS=: ./i_dir/**/*

See also

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