Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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?

share|improve this question
@MichaelKjörling: I had a look at extglob, but how do you include everything under a except a/b? – forthrin Aug 18 '13 at 18:53
Won't cd a && ls -d !(b/*) work? (To do it, just rm -r rather than ls -d.) – Michael Kjörling Aug 18 '13 at 18:55
Your suggestion deletes subfolders. I want to keep the folders intact. I want to find and delete all files in the tree under a (except files under a/b). – forthrin Aug 18 '13 at 19:16
So just skip the -r to rm. It seems what you are asking about is fairly easily answered by using bash's extended globbing, and then what you do with the result of the globbing is up to you. – Michael Kjörling Aug 18 '13 at 19:24
@MichaelKjörling Just because the two problems have vaguely-resembling solutions doesn't make the questions a duplicate. Most solutions to each of the two problems don't solve the other problem. – Gilles Feb 2 '15 at 18:47
up vote 6 down vote accepted

TL;DR: the best way is to use -exec rm instead of -delete.

find a \( -name b -prune \) -o -type f -exec rm {} +


Why does find complain when you try to use -delete with -prune?

Short answer: because -delete implies -depth and -depth makes -prune ineffective.

Before we come to the long answer first observe the behaviour of find with and without -depth:

$ find foo/

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 any foo/* and foo/bar before any foo/bar/*.

This can be reversed with -depth.

$ find foo/ -depth

Note that now foo/ appears after any foo/*. Same with foo/bar.

Longer answer:

  • -prune prevents find from descending into a directory. In other words -prune skips the contents of the directory. In your case -name b -prune prevents find from descending into any directory with the name b.
  • -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 entry b its contents has already been processed. Thus -prune is ineffective with -depth in effect.
  • -delete implies -depth so it can delete the files first and then the empty directory. -delete refuses to delete non-empty directories.

I guess it would be possible to add an option to force -delete to delete non-empty directories and/or to prevent -delete to imply -depth. But that is another story.

There is another way to achieve what you want:

find a -not -path "*/b*" -type f -delete

This may or may not be easier to remember. YMMV.

-path is required instead of -name because -name only matches against the name itself while -path is matching against the entire pathname.

Note that this will not delete any file which has any parent directory which name is starting with b but also any file which name is starting with b.

I assume that you used a and b as placeholders and the real names are more like allosaurus and brachiosaurus. If you put brachiosaurus in place of b then the amount of false positives will be drastically reduced.

There is still chance for false positives though, but at least they 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.

find a -not -path "*/b*" -type f -depth

Note that 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 directory b is huge.

share|improve this answer
-not -path was just the thing! Thanks for a generous explanation! – forthrin Aug 18 '13 at 18:55
Some elaboration on why -not -path works while -prune doesn't would be helpful. Why can -not -path co-exist with -depth? – Faheem Mitha Feb 2 '15 at 19:07

Just use rm instead of -delete:

find a -name b -prune -o -type f -exec rm -f {} +
share|improve this answer
Can you elaborate on why rm works and delete doesn't? – Faheem Mitha Feb 2 '15 at 18:50
Oh, I guess maybe because "-delete refuses to delete non-empty directories.", to quote @lesmana. So refuses to delete non-empty directories. But rm doesn't have that problem. But, regardless, elaboration would be a good thing. – Faheem Mitha Feb 2 '15 at 18:53
@FaheemMitha, the answer to that is in the question. -delete implies -depth, which obviously can't work with -prune. -path works, but doesn't stop find from descending in directories which it doesn't need to explore. – Stéphane Chazelas Feb 2 '15 at 19:23

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.