How to stop the find command after first match?

Is there a way to force the find command to stop right after finding the first match?

Asked By: coffeMug

||

With GNU or FreeBSD find, you can use the -quit predicate:

find . ... -print -quit

The NetBSD find equivalent:

find . ... -print -exit

If all you do is printing the name, and assuming the filenames don’t contain newline characters, you could do:

find . ... -print | head -n 1

That will not stop find after the first match, but possibly, depending on timing and buffering upon the second match or (much) later. Basically, find will be terminated with a SIGPIPE when it tries to output something while head is already gone because it has already read and displayed the first line of input.

Note that not all shells will wait for that find command after head has returned. The Bourne shell and AT&T implementations of ksh (when non-interactive) and yash (only if that pipeline is the last command in a script) would not, leaving it running in background. If you’d rather see that behaviour in any shell, you could always change the above to:

(find . ... -print &) | head -n 1

If you’re doing more than printing the paths of the found files, you could try this approach:

find . ... -exec sh -c 'printf "%sn" "$1"; kill -s PIPE "$PPID"' sh {} ;

(replace printf with whatever you would be doing with that file).

That has the side effect of find returning an exit status reflecting the fact that it was killed though.

We’re sending the SIGPIPE signal instead of the default SIGTERM to avoid the message that some shells display when parts of a pipe line are killed with a signal. They generally don’t do it for deaths by SIGPIPE, as those are naturally happening (like in find | head above…).

Answered By: Stéphane Chazelas

For entertainment purposes, here’s a lazy find generator in Bash. This example generates a ring over the files in the current directory. Read however many you want then kill %+ (maybe just 1)

#!/usr/bin/env bash
unset -v files n
trap 'kill "$x_PID"' EXIT

coproc x while :; do
    find . -type f -maxdepth 1 -exec sh -c "$(</dev/fd/3)" _ {} +
done 4<&0 <<EOF 3<&0 <&4-
for x; do
    read -r _
    printf '%s' "$x"
done
EOF

while
    echo >&${x[1]}
    IFS= read -rd '' -u "$x" 'files[n++]'
do
    printf '%q ' "${files[@]}"
    echo
    sleep .2
done
Answered By: ormaaj
find . -name something -print -quit

Terminates find after the first match after printing it.

Terminate find after a specific amount of matches and print results:

find . -name something -print | head -n 5

Surprisingly enough – head now terminates string after 5 matches, though I do not know how or why.

It is very easy to test. Just let find search a on root which would result thousands, maybe even more matches while taking at least a minute or more.
But when piped into “head” “find” will terminate after the specified amount of lines defined in head (default head shows 10, use “head -n” to specify lines).

Note that this will terminate after “head -n” reaches the specified newline character count and therefore any match that contains multiple newline characters will count accordingly.

Answered By: TheUnseen

grep also returns if used with the flag -m, so with

find stuff | grep -m1 .

it will return after the first line printed by find.

The difference between this and find stuff -print -quit | head -1 is that if the search is fast enough grep might not be able to stop the process in time (doesn’t really matter though), while if the search is long it will spare find to print a lot of not needed lines.

this instead works with busybox find, although since busybox grep also has -m it is not really needed

find /tmp/stuff -exec "sh" "-c" "eval 'echo {}; { kill $PPID; }'" ;

this will spit out a message about the find process having received the (usually) sigterm signal, but this output belongs to the running shell, not the find command so it does not mess with command output, meaning pipes or redirects will output just the line matched by find.

Answered By: untore
find  path -name something | awk 'NR == 1'
Answered By: Praveen Kumar BS

I Didn’t catch a bit of the ring stuff answer, so i re-implemented like this:
takes 3 arguments
1st: working directory
2nd: extension to find
3rt: number of results to show

#!/usr/bin/env bash
if [ -z "${1}" ]
then
    CWD="$(pwd)"
else
    CWD="${1}"
fi

if [ -z "${2}" ]
then
    EXT="wma"
else
    EXT="${2}"
fi

if [ -z "${3}" ]
then
    MAX=1
else
    MAX="${3}"
fi

TMPFILE=$(tempfile -p mtfind-)
find ${CWD} -iname "*.${EXT}" -type f 2>/dev/null 1>${TMPFILE} &
PID=$!
WCL=0
CONDITION=1
while [ ${CONDITION}  ]
do
    WCL=$(wc -l ${TMPFILE} | cut -d " " -f1)    
    CONDITION=$(echo "${WCL}<${MAX}"| bc -l)
    if [ -d "/proc/${PID}" ]; then
        echo -ne "."
    else
        echo "find ended"
        head -n${MAX} "${TMPFILE}"
        rm -f ${TMPFILE}
        exit
    fi
    sleep .02
done
kill -9 ${PID} 
head -n${MAX} "${TMPFILE}"
rm -f ${TMPFILE}
Answered By: user1016724

When running find with -exec use -quit after the ;, otherwise the command execution will be skipped. For example:

find -name "myfile*" -exec echo "Found {} and Quit" ; -quit
Found ./myfile1 and Quit

But when using -skip before -exec:

find -name "myfile*" -quit -exec echo "Found {} and Quit" ;
# Nothing was executed
Answered By: Noam Manos
Categories: Answers Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.