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:
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
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
,
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,
-exec
,-ok
, or
( 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
Linux: Block Until a String Is Matched in a File ("Tail + Grep with Blocking")
Capture Both Exit Status and Output from a System Call in R
How to Run the Cron Job as a User Instead of Root User
Add a Header to a Tab Delimited File
Numa Aware Cache Aligned Memory Allocation
Find and Remove Files with Space Using Find Command on Linux
How to Send Multicast Packets via a Specfic Interface in Linux
Gdb Can Not Open Shared Object File
Dynamic Loading and Weak Symbol Resolution
Inotify - How to Find Out Which User Has Modified File
Linker Error on Linux: "Undefined Reference To"
Advice Regarding Installing Arm Toolchain on Ubuntu Vm (64Bit)
How to Set Java Classpath in Linux
Clean Server Infected with C3284D Virus, Using Search and Replace