Note the second link ("about stopping searches at the first positive in a path tree") is not really relevant because a solution that ignores it may empty ./path/to/foo/again/foo
and then empty ./path/to/foo
; or it may empty ./path/to/foo
first and not get to ./path/to/foo/again/foo
at all (because this path does not exist now). In any case the goal will be reached.
Anything under (some) foo
matches -path '*/foo/*'
, but the ./path/to/foo
itself does not match. find
does not add trailing slashes to paths it creates and examines. An exception would be something like find /path/to/foo/ …
where /path/to/foo/
will be processed with a trailing slash and it will match -path '*/foo/*'
. You may consider -path '*/foo/?*'
, but if you start from /path/to/foo//
(which is a valid pathname) then it will match.
So maybe -path
and -name
?
find . -path '*/foo/*' ! -name foo
Will it give you a good list of pathnames to delete? Note ./path/to/foo/again/foo
will not be on it. The list will contain ./path/to/foo/again
, so if you delete again
properly then ./path/to/foo/again/foo
will be deleted anyway. Not bad, until you realize there will be no entry on the list that allows you to delete ./path/to/foo/foo/foo
(if such path exists).
Another problem with -path '*/foo/*'
is when you supply a starting point in a form of /a/foo/starting/point
. In such case every path considered by find
will match. In effect you may inadvertently remove the entire point
, while your intention was to empty /a/foo/starting/point/whatever/foo/
.
If you can cd
to the starting point then this should be safe:
cd /whatever/the/starting/point
# check if cd succeeded (the check can be automated with cd … && find … )
find . -path '*/foo/?*' -delete
(?
is most likely not needed because the specification explicitly states there should be no trailing slash in the portion of the pathname relative to what we supplied as the starting path; but I don't know if all implementations of find
in a wild strictly obey; so ?
is just in case).
Even if foo
appears in the path to the starting/point
, it wont matter. In find
what -path
tests is the pathname build by the tool, not the actual absolute pathname. When find starts from .
then all pathnames it builds will start with .
and -path
will test them as strings.
Note -delete
is not portable. A portable solution may be one of these:
find . -depth -path '*/foo/?*' -exec rm -r {} +
find . -path '*/foo/?*' -exec rm -r {} \;
The first one works like -delete
(which implies -depth
): it processes a directory only after all its content has been processed. rm
will get pathnames one by one in the right order and it will not really need to work recursively because any directory it tries to remove should already be empty (still -r
is needed so it can remove directories). Multiple pathnames will be supplied to a single rm
thanks to +
, still there will probably be more than one rm
because you cannot run rm
with arbitrarily many arguments. find -exec … +
knows this and can execute as many commands (rm
processes in our case) as it needs to process arbitrarily many entries and not hit the limit (for comparison: xargs
also knows this).
In many cases the most optimal portable solution would be to run one or few rm -r
processes that remove non-directories and directories directly under ./path/to/foo/
, so find
does not even need to go deeper. This is what the second command does. Note there is ;
(escaped from the shell) instead of +
. We don't want +
because it might happen find
decides the content of ./path/to/foo/
is not enough to run rm
with many arguments yet, it goes deeper to add more arguments, finds the content of ./path/to/foo/again/foo/
, only then it starts rm
. So rm -r
gets ./path/to/foo/again
and (possibly among others) ./path/to/foo/again/foo/whatever
, it removes the former (recursively), then it complains the latter does not exist. Therefore when without -depth
, it's better to run one rm
per pathname, so -exec … \;
.
But even then files that are no longer there will probably trigger warnings; not from rm
but from find
. It's because find
learns about the content of a directory before processing the directory. It does this even if it's going to process the directory first (i.e. even if there is no -depth
). After rm
removes ./path/to/foo/again
find
will still try to process ./path/to/foo/again/whatever
. Any solution with (explicit or implied) -depth
is free from this problem.
Use the first portable form, the one with -depth
; or better the form with -delete
, if only your find
supports -delete
.
Side note: if you want to empty a specific directory then cd
to it and:
# check if in the right directory
find . ! -name . -delete
With common implementations of find
you don't even need ! -name .
because they don't delete .
and it's by design. The command simplifies to:
find . -delete