I want to recursively delete all files not accessed in a while in folder a
, except all files in the subfolder b
.
find a \( -name b -prune \) -o -type f -delete
However, I get an error message:
find: The -delete action automatically turns on -depth, but -prune does
nothing when -depth is in effect. If you want to carry on anyway,
just explicitly use the -depth option.
Adding -depth
causes all files in b
to be included, which must not happen.
Anyone know a safe way to make this work?
Best Answer
one way is to use
-exec rm
instead of-delete
.alternatively use
-not -path
instead of-prune
:Explanation why
-prune
collides with-delete
:find complains when you try to use
-delete
with-prune
because-delete
implies-depth
and-depth
makes-prune
ineffective.observe the behaviour of find with and without
-depth
:There is no guarantee about the order in a single directory. But there is a guarantee that a directory is processed before its contents. Note
foo/
before anyfoo/*
andfoo/bar
before anyfoo/bar/*
.This can be reversed with
-depth
.Note that now all
foo/*
appear beforefoo/
. Same withfoo/bar
.more explanation:
-prune
prevents find from descending into a directory. In other words-prune
skips the contents of the directory. In your case-name b -prune
means that when find reaches a directory with the nameb
it will skip the directory including all subdirectories.-depth
makes find to process the contents of a directory before the directory itself. That means by the time find gets to process the directory entryb
its contents has already been processed. Thus-prune
is ineffective with-depth
in effect.-delete
implies-depth
so it can delete the contents first and then the empty directory.-delete
refuses to delete non-empty directories.Explanation of alternative method:
This may or may not be easier to remember.
This command still descends into the directory
b
and proceses every single file in it only for-not
to reject them. This can be a performance issue if the directoryb
is huge.-path
works differently than-name
.-name
only matches against the name (of the file or directory) while-path
is matching against the entire path. For example observe the path/home/lesmana/foo/bar
.-name bar
will match because the name isbar
.-path "*/foo*"
will match because the string/foo
is in the path.-path
has some intricacies you should understand before using it. Read the man page offind
for more details.Beware that this is not 100% foolproof. There are chances of "false positives". The way the command is written above it will skip any file which has any parent directory which name is starting with
b
(positive). But it will also skip any file which name is starting withb
regardless of position in the tree (false positive). This can be fixed by writing a better expression than"*/b*"
. That is left as an exercise for the reader.I assume that you used
a
andb
as placeholders and the real names are more likeallosaurus
andbrachiosaurus
. If you putbrachiosaurus
in place ofb
then the amount of false positives will be drastically reduced.At least the false positives will be not deleted, so it will be not as tragic. Furthermore, you can check for false positives by first running the command without
-delete
(but remember to place the implied-depth
) and examine the output.