5

There's the find . -type d -empty command, but that only finds literally empty directories.

What I want to achieve is something a little bit more complex: I want to find empty directories or directories that ONLY have other directories or empty directories, and this rule should be passed on recursively.

So for example, in this directory structure: ~1/11/111/ It would find 1/, 1/11/ and 1/11/111/ But if at any point in the tree there's a file, for example in this case if there's a file in 1/11/111/file1 then none of them before the file should be returned. So if there's an empty directory 1/11/111/1111/ next to 1/11/111/file1, 1111 should be returned.

The reason I want this is because I want to delete my empty folders.

Now I could probably do that by running find . -type d -empty -delete over and over again, but I want a way to kind of visualize it before I remove them.

Is this possible?

5
  • If, eventually, you're going to remove all the empty directories, then you can just iterate the tree depth-first, and remove them as you see them. With the subdirectories removed, the parent directories would then be empty, too. I think there are questions about that here on the site
    – ilkkachu
    Jul 23, 2019 at 7:47
  • just find . -depth -type d -empty -delete might even do
    – ilkkachu
    Jul 23, 2019 at 7:47
  • 1
    @ilkkachu The -depth is implicit with -delete.
    – Kusalananda
    Jul 23, 2019 at 7:48
  • 1
    questions as this, How to remove all empty directories in a subtree?, but then I just realized you wanted to print them before deleting, and I suppose that's slightly harder
    – ilkkachu
    Jul 23, 2019 at 7:51
  • I'd do this by piping find through sort into an awk-script to filter out directories having content (take a half hour or so, which is longer than I'd spend for this category). Aug 29, 2020 at 12:51

2 Answers 2

5

To see the directories deleted, just insert -print:

find . -type d -empty -print -delete

This would delete any sub-hierarchy beneath the current directory that is empty, while displaying the pathnames of the directories that are deleted.

The -delete action implies -depth, i.e. the traversal will start at the bottom of the directory structure and work its way up (a depth-first-traversal). This means that any directory that may be considered for deletion will already have its sub-directories processed (and these wil have been deleted if they were empty).

Example:

$ mkdir -p 1/2/3/4/5/6
$ tree
.
`-- 1
    `-- 2
        `-- 3
            `-- 4
                `-- 5
                    `-- 6

6 directories, 0 file
$ find . -type d -empty -print -delete
./1/2/3/4/5/6
./1/2/3/4/5
./1/2/3/4
./1/2/3
./1/2
./1
$ tree
.

0 directory, 0 file

If one directory has a file:

$ mkdir -p 1/2/3/4/5/6
$ touch 1/2/3/file
$ tree
.
`-- 1
    `-- 2
        `-- 3
            |-- 4
            |   `-- 5
            |       `-- 6
            `-- file

6 directories, 1 file
$ find . -type d -empty -print -delete
./1/2/3/4/5/6
./1/2/3/4/5
./1/2/3/4
$ tree
.
`-- 1
    `-- 2
        `-- 3
            `-- file

3 directories, 1 file

To list the directories that only contain subdirectories, but without deleting them (using bash):

shopt -s globstar
for dir in ./**/; do
    if [ -z "$(find "$dir" ! -type d -exec echo x \;)" ]; then
        printf '%s\n' "$dir"
    fi
done

This would loop over all the subdirectories in the current directory (reclusively, using the ** shell glob), and then try to find something that is not a directory in any of them. If something is found, then that directory is not empty, otherwise the pathname of the empty directory is outputted.

With GNU find, you could add -quit to the very end of the above find command (after \;) to speed things up a bit.

3
  • Thanks! That's more or less what I'm looking for. Just one more question: since -delete is recursive, is -exec also recursive? Also, since the -print option only seems to work with the -delete option, is there any way to list them all the way I wanted without deleting them? EDIT: Nevermind I can make it do that with the -depth argument.
    – user361323
    Jul 23, 2019 at 7:58
  • @user361323 find is recursive over the search paths that you give it. The -delete or -exec will just act on the current pathname being investigated at the moment. To list the empty directories without deleting them, just remove the -delete action (this will list the directories that are empty, but not the ones that contain only directories).
    – Kusalananda
    Jul 23, 2019 at 8:07
  • @user361323 See added bit at the end.
    – Kusalananda
    Jul 23, 2019 at 8:19
2

To print the empty directories without deleting any, we'll have to do more than just remove the empty ones from the leaves up.

Something like this may work:

find dir/ -type d ! -exec sh -c \
  'find "$1" ! -type d -print -quit | grep -q . >/dev/null' sh {} \; -print

The outer find finds each directory, and runs another find on each of them. The inner find looks to see if that directory (recursively) contains any non-directories, and returns a true value if so. The outer find then prints each directory that returned false (i.e. didn't contain any non-directories).

That's rather slow, though, with the recursive finds. A better solution might involve caching directories where files were found, or an approach that would start from empty directories and work up until a branch that contains file(s).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .