2

In general, if we want to execute a command if find identifies files corresponding to certain specs, we can use -exec. But in my case I want to exit the shell/script if find found a match and -exec can't do that. [Apparently find's -exec cannot call bash functions either.)

For instance in a script to remove a tree if and only if it contains only empty directories, I'd like to first check for presence of any non-directory and abort with an error message at the first such unexpected entry.

So far whenever I need to do something like this, I put the output to a variable (possibly using -quit with find) and testing for it being empty or not.

So my script looks like:

#! /bin/sh
INTRUDER="$(find "$@" ! -type d -print -quit)"
if [ -n "$INTRUDER" ] ; then
    echo "Found non-dir $INTRUDER; leaving the arguments untouched"
    exit 1
fi
rm -R "$@"

Is there a different/better approach than this? By "better" I mean, smaller to code, more portable (if there is any portability issue with the above), etc. By "different" I mean by avoiding the extra variable and testing for its length.

jamadagni
  • 1,331
  • 1
  • 12
  • 21
  • possible duplicate of [How to stop the find command after first match?](http://unix.stackexchange.com/questions/62880/how-to-stop-the-find-command-after-first-match) – cuonglm Sep 19 '14 at 04:02
  • 3
    Hello and thanks for your attention. I (already noted that I) know about `-quit`. But it merely stops searching after first match. I am asking for a workaround to not being able to `-exec` a shell built-in (for this particular example to exit the shell, not exit the `find`). So this is not a dup. Please remove that tag. – jamadagni Sep 19 '14 at 04:12

1 Answers1

2

This one worked for me:

find "$@" ! -type d -exec kill -9 $$ \; -quit && rm -R "$@"
  • If find exits normally (nothing is found) rm -R "$@" will be executed.
  • If find finds something the current shell/script is killed ($$ stores the pid). The rm-part will never be executed in this case.
chaos
  • 47,463
  • 11
  • 118
  • 144
  • Hi -- that's a neat trick! I'll wait and see if there's another answer though. – jamadagni Sep 19 '14 at 07:34
  • ... and it's limited to stopping the current script and doesn't allow to output an exit status, does it? – jamadagni Sep 19 '14 at 08:13
  • If the rm part is executed the command returns the exit status of `rm` (0, 1 or 2). If it's killed the return value `$?` (of the script) is 137 (see http://www.tldp.org/LDP/abs/html/exitcodes.html) 137 means 128+9 that the script is killed by signal #9 (`SIGKILL`). – chaos Sep 19 '14 at 08:38
  • @jamadagni You can trap the signal if you want to change the exit status. – Gilles 'SO- stop being evil' Sep 19 '14 at 21:33
  • @chaos: I'm sure a killed app will have an exit status. I meant about controlling the exit status like Gilles said. @Gilles: Thanks. Using `trap "exit 1" SIGKILL` does fine. – jamadagni Sep 25 '14 at 13:17