Is there a way in linux to look through a directory tree for only those directories that are the ends of branches (I will call them leaves here), i.e., dircetories with no subdirectories in them? I looked at this question but it was never properly answered.

So if I have a directory tree

├── branch1
│   ├── branch11
│   │   └── branch111   *
│   └── branch12        *
└── branch2
    ├── branch21        *
    └── branch22
        └── branch221   *

can I find only the directories that are the end of their branch (the ones marked with*), so looking only at the number of directories, not at the number of files? In my real case I am looking for the ones with files, but they're a subset of the 'leaves' that I want to find in this example.

  • please clarify: "no matter how many files they contain" vs " I am looking for the ones with files"
    – pLumo
    Jan 28, 2019 at 13:23
  • Sorry. That comment was because of the previous post, where the answer with the most votes looked for empty directories vs directories with subdirectories.
    – alle_meije
    Jan 28, 2019 at 13:30
  • so you explicitly want to look for not (!) empty directories ? Then you should accept Bodo's answer.
    – pLumo
    Jan 28, 2019 at 14:00
  • No, I mean that I am looking for directories that don't contain subdirectories, irrespective of whether or not they contain files.
    – alle_meije
    Jan 28, 2019 at 14:38
  • 2
    but isn't that exactly what the most upvoted answer from your linked question does.
    – pLumo
    Jan 28, 2019 at 14:41

4 Answers 4


To find only those leaf directories that contain non-directory files, you can combine an answer of the referenced question https://unix.stackexchange.com/a/203991/330217 or similar questions https://stackoverflow.com/a/4269862/10622916 or https://serverfault.com/a/530328 with find's ! -empty

find rootdir -type d -links 2 ! -empty

Checking the hard links with -links 2 should work for traditional UNIX file systems. The -empty condition is not part of the POSIX standard, but should be available on most Linux systems.

According to KamilMaciorowski's comment the traditional link count semantics for directories is not valid for Btrfs. This is confirmed in https://linux-btrfs.vger.kernel.narkive.com/oAoDX89D/btrfs-st-nlink-for-directories which also mentions Mac OS HFS+ as an exception from the traditional behavior. For these file systems a different method is necessary to check for leaf directories.

  • 4
    In Btrfs any directory reports link count of 1. I guess it's not a "normal UNIX file system" then… Jan 28, 2019 at 20:13
  • @KamilMaciorowski Thanks for the hint. I added a paragraph about exceptions.
    – Bodo
    Jan 29, 2019 at 7:51

You could use nested find and count number of subdirectories:

find . -type d \
  \( -exec sh -c 'find "$1" -mindepth 1 -maxdepth 1 -type d -print0 | grep -cz "^" >/dev/null 2>&1' _ {} \; -o -print \)
  • 1
    Tested on BTRFS, yes this works without relying on link-count=2. But it's not fast or efficient. It spawns sh to run a pipeline for every directory (empty or not) in the tree. Jan 29, 2019 at 5:11
  • To quantify "not fast or efficient," on a modern BTRFS HDD, 2328 results took: real 0m6.764s / user 0m3.865s / sys 0m6.215s. This is acceptable given the frequency of this search.
    – bvargo
    May 13 at 12:31

With zsh:

leafdirs=( **/*(NDFl2) )

Sets the $leafdirs array with the list of matching leaf directories.

You can print the list raw on 1 Column with:

print -rC1 -- $leafdirs

Or loop over them with:

for dir ($leafdirs) something with $dir

That's more or less a translation of @Bodo's GNU find answer, so like it works only on traditional Unix-like file systems like ext4 that implement . and .. as actual directory entries.

  • **/: any level of subdirectories
  • (NDFl2): glob qualifier
  • N: enable nullglob for that one glob: don't fail if there's no match. Instead, result in an empty list
  • D: enable dotglob for that one glob: look inside hidden dirs, and don't skip hidden files
  • F: select full directories: directories containing at least one entry other than . and .. (like GNU find's -type d ! -empty)
  • l2: only dirs with a link count of 2 (like -links 2). One for the dir's entry in its parent directory and one for . in it. Any subdir would add one to the link count because of the .. entry in those.

On file systems where nlinks == 2 cannot be used, you could do:

leafdirs=( **/*(NDF^e['()(($#)) $REPLY/*(ND/Y1)']) )

Where we use the e qualifier (negated with ^) to run the code that checks whether the directory (stored in $REPLY in that code) has subdirectories, here using an anonymous function to check whether the expansion of $REPLY/*(ND/Y1), the directories in $REPLY, stopping at the first match, passed as its arguments is a non-empty list ($# is non-zero).


If the */ filename globbing pattern expands to something that is not the name of a directory, then the current directory has no (non-hidden) subdirectories.

With find:

find root -type d -exec sh -c 'set -- "$1"/*/; [ ! -d "$1" ]' sh {} \; ! -empty -print

Note that this would treat a symbolic link to a directory in a leaf directory as a directory since the pattern would traverse the symbolic link.

The -empty predicate is not standard, but often implemented. Without it, you would do something similar as with detecting subdirectories:

find root -type d \
    -exec sh -c 'set -- "$1"/*/; [ ! -d "$1" ]' sh {} \; \
    -exec sh -c 'set -- "$1"/*;  [   -e "$1" ]' sh {} \; -print

Or, a bit more efficiently,

find root -type d -exec sh -c '
    set -- "$dir"/*/
    [ -d "$1" ] && exit 1
    set -- "$dir"/*
    [ -e "$1" ]' sh {} \; -print

Or, employing the -links predicate that I had forgotten about (thanks Bodo):

find root -type d \
    -links 2 \
    -exec sh -c 'set -- "$1"/*; [ -e "$1" ]' sh {} \; -print

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