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
3 Answers 3
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.
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 onesuser001– user0012017年11月09日 01:14:55 +00:00Commented Nov 9, 2017 at 1:14
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.