15

So the following behaviour of unix find just cost me dearly:

> touch foo
> touch bar
> ls  
bar  foo
> find . -name '*oo' -delete
> ls
bar
> touch baz
> ls
bar  baz
> find . -delete -name '*ar'
> ls
> #WHAAAT?

How does this make sense?

| improve this question | | | | |
15

The command line of find is made from different kinds of options, that are combined to form expressions.

The find option -delete is an action.
That means it is executed for each file matched so far.
As first option after the paths, all files are matched... oops!

It is dangerous - but the man page at least has a big warning:

From man find:

ACTIONS
    -delete
           Delete  files; true if removal succeeded.  If the removal failed, an
           error message is issued.  If -delete fails, find's exit status  will
           be nonzero (when it eventually exits).  Use of -delete automatically
           turns on the -depth option.

           Warnings: Don't forget that the find command line is evaluated as an
           expression,  so  putting  -delete first will make find try to delete
           everything below the starting points you specified.  When testing  a
           find  command  line  that  you later intend to use with -delete, you
           should explicitly specify -depth in order to avoid later  surprises.
           Because  -delete  implies -depth, you cannot usefully use -prune and
           -delete together.


From further up in man find:

EXPRESSIONS
    The expression is made up of options (which affect overall operation rather
    than  the  processing  of  a  specific file, and always return true), tests
    (which return a true or false value), and actions (which have side  effects
    and  return  a  true  or false value), all separated by operators.  -and is
    assumed where the operator is omitted.

    If the expression contains no actions other than  -prune,  -print  is  per‐
    formed on all files for which the expression is true.


On trying out what a find command will do:

To see what a command like

find . -name '*ar' -delete

will delete, you can first replace the action -delete by a more harmless action - like -fls or -print:

find . -name '*ar' -print

This will print which files are affected by the action.
In this example, the -print can be left out. In this case, there is not action at all, so the most obvious is added implicitly: -print. (See the second paragraph of the section "EXPRESSIONS" cited above)

| improve this answer | | | | |
  • I guess I was just an idiot for not reading the manual first.. But it seemed so straightforward to just add the -delete flag :-) But is there any reason anyone would ever do find . -delete rather than rm -rf .? Or does that just happen to be the way find parses and processes its arguments? – mszep Aug 17 '14 at 12:40
  • 1
    Oh, I fully agree that syntax is very dangerous - but that is hard to avoid, as it's just a special case of the comman line expression syntax of find, which is extremely powerful. Powerful as in "A powerful chainsaw". – Volker Siegel Aug 17 '14 at 13:12
  • doesn't the quoted manual page says warning: use -depth when testing stuff you later want to -delete which you didn't do on your practical side example? – pqnet Aug 17 '14 at 17:58
  • Oh, right - the problem is the heading - I meant to illustrate the use of print in the place of -delete like a -n option; The first command should not have any practical use at all, of course; I'll go change it. – Volker Siegel Aug 17 '14 at 18:03
9

In find argument order matters, a lot.

Arguments can be options, tests and actions. You should typically use options first, then tests, then actions.

Sometimes find even warns you about a possible bad ordering (for example when you use -maxdepth after other arguments), but others it seems it doesn't.

What find . -delete -name '*ar' does is:

  1. Finds files and directories upon current directory.
  2. Deletes them ALL as they are found!
  3. Then sees if they are named like '*ar' (this part has no effect now).

What you probably want to do is:

find -name '*ar' -delete

This will, for every file, see if it matches '*ar', and only if it satisfies the condition it will delete the file.

Sorry if you found out too late.

| improve this answer | | | | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.