19

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?

Braiam
  • 35,991
mszep
  • 293
  • 2
    This question is related, might be interesting for future readers. http://unix.stackexchange.com/questions/101282/how-does-the-order-of-arguments-influence-the-speed-of-find – Bernhard Aug 17 '14 at 11:28

2 Answers2

19

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)

Volker Siegel
  • 17,283
  • 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
11

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.

LatinSuD
  • 1,421