I am using find in a while loop. it takes a list of ips in ip.txt, searches for the ip in a directory of files and then writes the name of the file that each IP was found in to a file called results.res

I want find to stop and move onto the next ip after each succesful find.

My current script is:

while read ip; do
    find . -type f -maxdepth 1 -not -name ips.txt -not -name results.res -print0  | xargs -0 grep "$ip" >> results.res
done < ips.txt

I have researched other answers on the website and found that using

-print -quit
-print | head -n 1

stops find after the first succesful find, however because i'm using a while loop this does not work, can anybody suggest a working solution?

  • Your problem is interesting, but if you want just to get rid of it, you could probably use just grep -rlm1 "$ip" | head -n1 >> results.txt instead of find.
    – user313992
    Commented Apr 23, 2019 at 3:34

2 Answers 2

while IFS= read -r ip; do
  find . -maxdepth 1 \
         -type f \
         ! -name ips.txt \
         ! -name results.res \
         -exec grep -lwFe "$ip" {} \; \
done < ips.txt > results.res

Would read each line of the ips.txt file into the $ip variable and go through the files matching the find criteria (in no particular order), reporting the first file which contains $ip as a Fixed string and word delimited. -quit is invoked when -exec succeeds which is when grep returns a true exit status when a match is found.


The way your script is currently written, it will run find, then run grep for each file it finds, for every ip it finds in ips.txt.

You do not need to run find each time to get the same list of files, so take that out of the while loop and assign it to a variable. You also do not need to run grep for each file you find, you can search them all in one command. Now that you have the list of files, you can do a grep for each ip and only return the first match. So the script would look something like this now:

files=$(find . -maxdepth 1 -type f -not -name ips.txt -not -name results.res)
while read ip; do
    grep --with-filename --max-count=1 "$ip" $files | head -n 1 >> results.res
done < ips.txt

This will grep through each file from the find command for each ip in ips.txt and should put in lines that contain each IP address, and the first file in which it was found into results.res.


After some testing, there does seem to be a better way as the previous code does not handle filenames with spaces or odd characters at all. So I am going to get rid of find as it seems unnecessary here.

You are planning on greping through all files in the current directory except results.res and ips.txt, so you could use grep without find at all:

while read ip; do
    grep --with-filename --max-count=1 --exclude={results.res,ips.txt} "$ip" * | head -n 1 >> results.res
done < ips.txt
  • OP does the smart thing in using -print0 | xargs -0 for processing filenames; you should too.
    – muru
    Commented Apr 23, 2019 at 1:30
  • Using find within the while loop as in OP's question seemed terribly inefficient if they only want the first match. If you have an answer using find with -print0 | xargs -0 that fits the OP's desired behavior I will upvote it. Since file processing seems unnecessary in this particular instance, I have made an update that does not do it. Commented Apr 23, 2019 at 2:59
  • I did not say you had to use find within the loop. find ... -print0 > files; while ... do xargs -0a files grep ....
    – muru
    Commented Apr 23, 2019 at 3:02
  • Thanks for the response, however the number of files in the direcrtory i am searching through is too large for grep and i receive '/bin/grep: Argument list too long'. This is why i used find in the first place (sorry should of mentioned this). i their a solution without grep?
    – jonny b
    Commented Apr 23, 2019 at 10:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .