What Does -Prune Option in Find Do

How to use '-prune' option of 'find' in sh?

The thing I'd found confusing about -prune is that it's an action (like -print), not a test (like -name). It alters the "to-do" list, but always returns true.

The general pattern for using -prune is this:

find [path] [conditions to prune] -prune -o \
[your usual conditions] [actions to perform]

You pretty much always want the -o (logical OR) immediately after -prune, because that first part of the test (up to and including -prune) will return false for the stuff you actually want (ie: the stuff you don't want to prune out).

Here's an example:

find . -name .snapshot -prune -o -name '*.foo' -print

This will find the "*.foo" files that aren't under ".snapshot" directories. In this example, -name .snapshot makes up the [conditions to prune], and -name '*.foo' -print is [your usual conditions] and [actions to perform].

Important notes:

  1. If all you want to do is print the results you might be used to leaving out the -print action. You generally don't want to do that when using -prune.

    The default behavior of find is to "and" the entire expression with the -print action if there are no actions other than -prune (ironically) at the end. That means that writing this:

     find . -name .snapshot -prune -o -name '*.foo'              # DON'T DO THIS

    is equivalent to writing this:

     find . \( -name .snapshot -prune -o -name '*.foo' \) -print # DON'T DO THIS

    which means that it'll also print out the name of the directory you're pruning, which usually isn't what you want. Instead it's better to explicitly specify the -print action if that's what you want:

     find . -name .snapshot -prune -o -name '*.foo' -print       # DO THIS
  2. If your "usual condition" happens to match files that also match your prune condition, those files will not be included in the output. The way to fix this is to add a -type d predicate to your prune condition.

    For example, suppose we wanted to prune out any directory that started with .git (this is admittedly somewhat contrived -- normally you only need to remove the thing named exactly .git), but other than that wanted to see all files, including files like .gitignore. You might try this:

    find . -name '.git*' -prune -o -type f -print               # DON'T DO THIS

    This would not include .gitignore in the output. Here's the fixed version:

    find . -name '.git*' -type d -prune -o -type f -print       # DO THIS

Extra tip: if you're using the GNU version of find, the texinfo page for find has a more detailed explanation than its manpage (as is true for most GNU utilities).

What does -prune option in find do?

Try

find * -maxdepth 0 -name "efence*" -prune -o -print

The prune option does print matching files, if no other options are specified (it still prevents find from recursing into matching directories, however).

Edited to add explanation:

find expressions distinguish between tests and actions. From man find:

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 performed on all files for which the expression is true. [my emphasis]

So -prune is an action which has the side effect that find will not recurse into subdirectories which match the preceding test (in your case, -maxdepth 0 -name "efence*"). But in terms of the truth-value of the expression, it's equivalent to just having

find * -maxdepth 0 -name "efence*" -true

and since you didn't specify any other action, -print is assumed (this assumption is always present as it allows you to type e.g. find . -name "*.java" instead of find . -name "*.java" -print).

Hope that makes sense. The accepted answer at the other thread talks about the same thing.

How can this `find -prune`'s behavior be understood?

As written in this comment and this comment, my question, which is summarized in the Supplement section in OP, has come from the ambiguity in manpage of GNU find and POSIX has a better explanation. I found this is true.

POSIX says

(If no expression is present, -print shall be used as the expression. Otherwise,) if the given expression does not contain any of the primaries -exec, -ok, or -print, the given expression shall be effectively replaced by:

( given_expression ) -print

and it is natural to interpret given_expression is a compound expression which consists of one or more sub-expressions because it is closed in parenthesis. (If this given_expression referred to a single sub-expression, the parenthesis would definitely be redundant.)

How to use find and the prune option with an while loop

In while loop, it seems you are trying to rename files with extension .txt to .mailed. You can achieve the same using -exec option.

Try adding following portion to the end of your find command and remove piping to while loop.

-exec sh -c 'mv -f $0 ${0%.txt}.mailed' {} \;

Complete command would look like

find /opt/myTESTdir/ -iwholename '*lost+found' -prune -o -ctime +4 -type f -iname '*trace*' ! -iname '*.mailed*' -exec sh -c 'mv -f $0 ${0%.txt}.mailed' {} \;

how to correctly use prune? -type d and -type f have a different effect on prune?


find . -type f -path ./source/script -prune -o -print;

is interpreted as

find . (-type f AND -path ./source/script AND -prune) OR (-print);

find . -path ./source/script -prune -o -type f -print;

is interpretted as

find . (-path ./source/script AND -prune) OR (-type f AND -print);

Note that -print and -prune are expressions that evaluate to true.

So if (-path ./source/script AND -prune) is true then (-type f AND -print) is not evaluated and -print is not called. And `(-path ./source/script AND -prune) is true for all the files and subdirectories of ./source/script

Leaving out '-print' from 'find' command when '-prune' is used

The implicit -print applies to the entire expression, not just the last part of it.

% find . \( -path '*blue*' -prune -o -type f \) -print
./green/test.log
./green/yellow/config.txt
./green/yellow/test.log
./green/config.txt
./cyan/blue
./blue
./aqua/blue
./aqua/config.txt

It's not decending into the pruned directories, but it is printing out the top level.

A slight modification:

$ find . ! \( -path '*blue*' -prune \) -type f
./green/test.log
./green/yellow/config.txt
./green/yellow/test.log
./green/config.txt
./aqua/config.txt

(with implicit -a) would lead to having the same behavior with and without -print.

-prune in find working without OR(-o) option - Unix


-prune
Always evaluates to the value True. Stops the descent of the current path name if it is a directory. If the -depth flag is specified, the -prune flag is
ignored.

I think if you play with it, you can figure out what it is doing.

e.g.

find . ! -name . -prune

gives

./1.tst
./2.tst
./d

We don't go down into ./d because of the prune -- "Stops the descent ...". What is left is then filtered by the -name '*.tst' to be just the list files at the top directory.

HTH

How do I exclude a directory when using `find`?

Use the -prune primary. For example, if you want to exclude ./misc:

find . -path ./misc -prune -o -name '*.txt' -print

To exclude multiple directories, OR them between parentheses.

find . -type d \( -path ./dir1 -o -path ./dir2 -o -path ./dir3 \) -prune -o -name '*.txt' -print

And, to exclude directories with a specific name at any level, use the -name primary instead of -path.

find . -type d -name node_modules -prune -o -name '*.json' -print


Related Topics



Leave a reply



Submit