bash find, only delete files - order of arguments

209 Views Asked by At

Say today is April 8th and I execute the following in bash.

cd /tmp
mkdir hello
touch -d 2015-04-01 hello

Then, say I want to delete all files in /tmp that are older than one day, but NOT directories and I execute this:

find /tmp -mtime +1 -delete -type f

Why is directory "hello" deleted if it's not a file?

Thanks!

2

There are 2 best solutions below

0
On BEST ANSWER

The find command executes the expression in order. Since -delete is before -type, -type is never reached. Try:

find /tmp -mtime +1 -type f -delete 
0
On
  • David C. Rankin's helpful answer uses the correct abstract term, expression, to refer to the list of arguments starting with -mtime ....
  • The OP, by contrast, calls this list options [edit: in a since-deleted post].

Calling them "options" is understandable, but since the very fact that they're not options is the cause of the problem, find's terminology and concepts warrant a closer look:

  • The arguments that follow the input path(s) are collectively called an expression.
  • An expression is composed of:
    • tests (e.g., -type f)
    • actions (e.g., -delete)
    • options (e.g., -maxdepth 1) - note that such options are distinct from the standard options that must come before even the input paths (e.g., find -L /tmp ...)
    • Note: The above is GNU find terminology, which is helpfully more fine-grained than the one in the POSIX spec. for find, where all three constructs are called by a single name, primaries (BSD find also just uses primaries in its man page).
    • operators: -a (-and) for logical AND, -o (-or) for logical OR, and ! (-not) for negation; the alternative forms in parentheses are not POSIX-compliant, but supported by GNU and BSD find.
  • Operators combine tests and actions into Boolean expressions.
    • In the absence of explicit operators, tests and actions are joined by an implicit logical AND (-a).
    • -a and -o apply short-circuiting (see below)
    • Sub-expressions can be grouped with \( and \) to alter precedence (the \-escaping is to protect the parentheses from interpretation by the shell).
    • Precedence (highest first): \(...\), !, -a, -o
  • Order matters with respect to tests and actions.
    • find options, by contrast, are not positional, but GNU find by default issues a warning, if they aren't placed before tests and actions. To avoid the warning, and for conceptual clarity in general, it is better to do so.
  • Every test and action returns a Boolean, and short-circuiting applies:
    • In the typical case - with -a implied - this means that subsequent test and actions are NOT evaluated, once a previous test or action has returned false:
      • find . -false -print # !! -print is NOT executed
    • Similarly, the 2nd operand of an -o (-or) expression is NOT executed, if the 1st one has returned true:
      • find . -print -o -print # !! 2nd -print is NOT executed