16

When find is invoked to find nothing, it still exits with code 0. Is there a way to make it return an exit code indicating failure when no file was found?

XZS
  • 1,448
  • 1
    I don't think find directly supports it. You can do something like find ... -print0 | grep -qz ., perhaps, if your grep supports it. – muru Apr 23 '15 at 22:50
  • My grep is gnu grep so it supports this nice idea. Unfortunately, I also need the find output to be piped somewhere and I cannot replace the pipe with -exec. – XZS Apr 23 '15 at 22:56
  • I'm making something. – Aloha Apr 23 '15 at 22:57
  • 1
    @XZS you can skip the -q, then grep will simply pass through the data, while still breaking the pipeline and reporting a failure if nothing comes through. – muru Apr 23 '15 at 23:02
  • @muru A do-nothing grep guarding the pipe, very elegant. Turn this into an answer and it will be accepted. – XZS Apr 23 '15 at 23:19

3 Answers3

11

If your grep supports reading NUL-delimited lines (like GNU grep with -z), you can use it to test if anything was output by find:

find /some/path -print0 | grep -qz .

To pipe the data to another command, you can remove the -q option, letting grep pass on the data unaltered while still reporting an error if nothing came through:

find /some/path -print0 | grep -z . | ...

Specifically, ${PIPESTATUS[1]} in bash should hold the exit status of grep.

If your find doesn't support -print0, the use grep without -z and hope that newlines in filenames don't cause problems:

find ... | grep '^' | ...

In this case, using ^ instead of . might be safer. If output has consecutive newlines, ^ will pass them by, but . won't.

muru
  • 72,889
  • Grep's -z is a GNU extension. Do you have something for more Posixy? –  Oct 21 '17 at 09:11
  • @jww unlikely, since with pure POSIX, I don't think you can detect a failure in the middle of a shell pipe. If you have a shell which can do that (via PIPE_STATUS or something similar), then it probably has a read which can do null-delimited input; then you can read a single line, fail if empty; or print it back out and cat the rest. (Assuming you want to pass on the data to something else, otherwise you can probably do find ... -exec echo foo {} \+ | grep -q foo.) – muru Oct 22 '17 at 14:17
  • Is -z just to make sure strange characters like \r are preserved? If piping through grep isn't desired, or there are no weird filenames, then is grep -z needed? – mwfearnley Sep 29 '22 at 12:38
4

You ask specifically for a return code... which I don't see in options. But this is how I solved it (because grep -z is not on Mac port):

Gives code 0 if 1 line was found

test 1 == `find */.kitchen/ -name private_key | wc -l`

So...

if [ 0 == `find */.kitchen/ -name my-file.txt | wc -l` ] ; then
   echo "Nothing found"; exit;
fi

Also, as a generic solution, this might be useful:

Check if pipe is empty and run a command on the data if it isn't

0

I am very late to this question, but I just found an elegant answer :-)

find . -type d -empty -and -not -name . -print -exec rmdir {} + | \
  awk '{print} END {if (NR == 0) {exit(1)}}'

find here looks for empty directories in the current directory,
removes the empty directories,
prints the name of removed directories to the pipe,
awk prints the output of find and if no output is found NR == 0, bails out with an error code.

This means I can now iteratively remove empty directories:

while find . -type d -empty -and -not -name . -print -exec rmdir {} + | \
  awk '{print} END {if (NR == 0) {exit(1)}}'; \
do echo; done

Replace "my" find parameters with yours, keep the awk part either as is or without the {print} if you do not want to see the output of find in the terminal, and "voilà".

asoundmove
  • 2,495