0

I am trying to search all files in a directory (and all subdirectories) for a string. If the file has that string, I then want to search for the number of times another string occurs, in this case "snake" and output it. I would also like to output the lines on which "snake" occurs within the file.

echo "Files with Reptile:"
find . -type f -name "*.txt" -exec grep -l "Reptile" {} \;

My challenge is how I add additional grep commands after the curly brackets. I have tried a couple of things and keep getting a "missing argument to -exec" error.

Am I trying to stuff too much into this find?

Thanks

1

You could try the following:

$ find . -type f -name "*.txt" -exec sh -c "grep -l "Reptile" {} | xargs -I% grep -Hn snake %" \;
./rep1.txt:2:snake
./rep2.txt:5:another snake

Output contains colon-delimited lists in which the first argument is the file name (from the -H argument to grep), the second argument is the line number on which the desired term appears (from the -n argument to grep), and the third argument is the line itself.

The xargs can be moved outside the find, giving you:

$ find . -type f -name "*.txt" -exec grep -l "Reptile" {} \; | xargs -i grep -Hn snake {}

Note that the -i argument to xargs (which is equivalent to -I{} is deprecated but I use it often for convenience.

Input files:

$ tail -n+1 rep*.txt
==> rep1.txt <==
Reptile
snake
iguana
crocodile

==> rep2.txt <==
Reptile
alligator
turtle
another snake
komodo dragon

==> rep3.txt <==
Reptile
lizard
gecko

If you need to deal with malformed file names, you can think about incorporating print0 and the -0 option to xargs.

1

Multiple -exec predicates. Since -exec is only true if the command succeeds, you don't need to do anything special with the first grep.

find ... -exec grep -q "Reptile" {} \; -exec grep -Hn snake {} \;
  • +1 for pointing out that -exec predicates are conditionally dependent on preceding ones – user001 Nov 9 '17 at 1:14
0
find . -type f -name "*.txt" -exec sh -c 't=$(mktemp)
        for fi
        do
                if grep -q Reptile "$fi"
                then
                        echo "$fi contains Reptile"
                        grep snake "$fi" | tee "$t"
                        echo "snake appeared on $(wc -l < "$t") line(s)"
                fi
        done
        rm -f "$t"
        ' sh {} +

This reports the names of files that contain the word/string “Reptile”, even if they don’t contain the word “snake”, and independently reports the number of lines that contain “snake”.

Oops; I misread the question, and thought that was a requirement.  Taking it out is easy:

find . -type f -exec sh -c '
        for fi
        do
                if grep -q Reptile "$fi"
                then
                        echo "$fi contains Reptile"
                        grep snake "$fi"
                fi
        done
        ' sh {} +

You can add a -n option to the grep snake if you want.

This should handle just about any valid filename.  Check the other answer(s) with filenames beginning with space and/or containing newline.

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.