From the find
manpage:
-depth
Process each directory’s contents before the directory itself. The -delete
action also implies -depth
.
With
find . -type d -depth -empty -exec rmdir "{}" \;
find
starts in the current directory, and looks for all files and directories contained therein (and in children etc.); it then processes each one, starting with the contents of a directory before processing the parent directory. Imagine the following tree:
.
├── a
│ ├── c
│ └── d
│ └── e
└── b
find . -depth
shows the order in which this will be processed:
./a/c
./a/d/e
./a/d
./a
./b
.
As you can see, the children are listed before the parents. In a more complex find
command, the actions would be applied to children before parents; in the example you give, empty leaf directories will be processed first, and if they’re empty, deleted; then their parents will be processed, and in turn if they’re empty (which might be the case since their empty children have been deleted), deleted too, and so on all the way back to the current directory.
If you omit -depth
, find
processes files in enumeration order, for example
.
./a
./a/c
./a/d
./a/d/e
./b
So .
is processed first; it’s not empty so it’s left alone. Then a
, and the same applies. Then a/c
; that gets deleted since it’s empty. a/d
is left alone, a/d/e
is deleted, as is b
. But find
doesn’t revisit the directories it’s already processed, so a
and a/d
remain even though we want them to be deleted.
-exec rmdir {} +
instead of-exec rmdir {} \;
to avoid running onermdir
per directory as that would delay the running sofind
would not delete the parent as it would not be empty at the time of the check (it would become empty later whenrmdir
is run when enough arguments have been accumulated). With somefind
implementations including yours, you cause use-delete
(which implies-depth
as noted, sofind . -type d -empty -delete
would be enough). That saves a rmdir execution and also removes some race conditions. – Stéphane Chazelas Dec 08 '17 at 16:08