1

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?

  • 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 at 7:47
  • just find . -depth -type d -empty -delete might even do – ilkkachu Jul 23 at 7:47
  • 1
    @ilkkachu The -depth is implicit with -delete. – Kusalananda Jul 23 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 at 7:51
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).

1

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.

  • 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 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 at 8:07
  • @user361323 See added bit at the end. – Kusalananda Jul 23 at 8:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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