2

From https://unix.stackexchange.com/a/24163/167166

find . -type d -depth -empty -exec rmdir "{}" \;

That will first drill down the directory tree until it finds the first empty directory, then delete it. Thus making the parent directory empty which will then be deleted, etc. This will produce the desired effect (I do this probably 10 times a week, so I'm pretty sure it's right). :-)

How does it work so that the parent directory will be deleted after its subdirectories are deleted?

Thanks.

Tim
  • 101,790

1 Answers1

8

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.

Stephen Kitt
  • 434,908
  • 5
    As an additional note: here one can't use -exec rmdir {} + instead of -exec rmdir {} \; to avoid running one rmdir per directory as that would delay the running so find would not delete the parent as it would not be empty at the time of the check (it would become empty later when rmdir is run when enough arguments have been accumulated). With some find implementations including yours, you cause use -delete (which implies -depth as noted, so find . -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