16

Is there an idiomatic means to detect whether "find" found any matches? I'm currently using

COUNT=`find ... | wc -l`
if [ "$COUNT" -gt 0 ]; then

but this seems a little indirect to me. Also, I'd like find to stop searching once it's found a match, so it doesn't waste time and effort. I just need to know whether or not there are any files that match.

Update: I made the mistake of writing my question without the code in front of me: I use wc -l in a different case, where I need to know the total number of found files anyway. In the case where I'm only testing for whether there are any matches, I was using if [ -z $(find …) ].

4 Answers 4

17

If you know you have GNU find, use -quit to make it stop after the first match.

Portably, pipe the output of find into head -n 1. That way find will die of a broken pipe after a few matches (when it's filled head's input buffer).

Either way, you don't need wc to test whether a string is empty, the shell can do it on its own.

if [ -n "$(find … | head -n 1)" ]; then …
4
  • Interesting... I thought -n only applied to 'defined' variables ($abc, $xyz, etc), but I suppose $(...) is a 'defined' temp variable.. +1 ... (and I just read recently that using -n is risky, unless you are sure that the variable has not already been declared and is 'unused)... but a temp won't have this possibility... ie. uninitialized vs. empty
    – Peter.O
    Commented May 27, 2011 at 8:09
  • 2
    @fred: On the contrary, -n tests whether a string is empty, it has nothing to do with variables. What you've read was probably saying that using -n doesn't test whether a variable is defined: -n "$foo" is false if foo is defined but empty, and displays an error message under set -u. [ -n "${foo+1}" ] reliably tests whether foo is set, but treating empty and unset variables identically is usually a good idea: [ -n "${foo:+1}" ], or just [ -n "$foo" ] if you're not worried about set -u. Commented May 27, 2011 at 10:01
  • Thanks! I don't have -quit available, but piping to head -n 1 works (as does head -c 1, which stops even earlier, but probably with negligible benefit). Also, I made the mistake of writing my question without the code in front of me: I use wc -l in a different case, where I need to know the total number of found files. In the case where I'm only testing for whether there are any matches, I was using if [ -z `find … ` ].
    – Chris Page
    Commented Jun 1, 2011 at 7:04
  • Just head -1 also works.
    – Alexander
    Commented Apr 19, 2022 at 13:36
7

You can use the -quit action to stop after the first match. You'll probably want to combine that with another action (like -print) or you won't be able to tell whether it found anything.

For example, find ... -print -quit will print the path of the first matching file and then exit. Or, you could use -printf 1 -quit to print 1 if there's a match and nothing if there isn't.

find's exit status reflects whether there were errors while searching, and not whether it found anything, so you have to check its output to see if there's a match.

2
  • -quit is a GNU extension. It won't work on systems using other implementations of find(1), such as OS X, the BSDs, and probably most commercial Unices. Commented May 27, 2011 at 1:25
  • Thanks, that's useful to know in case I'm using another system. Unfortunately, neither -quit nor -printf are available on mine (Mac OS X).
    – Chris Page
    Commented May 27, 2011 at 1:53
4

Exit 0 is easy with find, exit >0 is harder because that usually only happens with an error. However we can make it happen:

if find -type f -exec false {} +
then
  echo 'nothing found'
else
  echo 'something found'
fi

Note that this solution is more performant than using a subshell; execing false is certainly faster than execing even Dash:

$ cat alfa.sh bravo.sh charlie.sh delta.sh
find -name non-existing-file -exec false {} +
find -name existing-file -exec false {} +
[ "$(find -name non-existing-file)" ]
[ "$(find -name existing-file)" ]

$ strace dash alfa.sh | wc -l
807

$ strace dash bravo.sh | wc -l
1141

$ strace dash charlie.sh | wc -l
1184

$ strace dash delta.sh | wc -l
1194
0
0

You can wrap it into the shell condition, e.g.:

[ "$(find . ...)" '!=' '' ] && echo Found || echo Not found

Where ... is your match condition, e.g. -name *.txt.

Some other examples:

[ "$(find /etc -name hosts)" ] && echo True || echo False
[ ! -z "$(find /etc -name hosts)" ] && echo True || echo False

You must log in to answer this question.

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