Ubuntu: Display all the folders which do not have any sub directory [duplicate]



Question:

This question already has an answer here:

How do I display all the folders which do not have any sub directory using the terminal?


Solution:1

Try this:

find . -type d -print0 | sort -z | gawk '{a[$0]; sub(/[/][^/]*$/, ""); if ($0 in a) delete a[$0]} END{for (d in a)print d}' RS='\0'  

How it works

To be safe for all possible directory names, we use nul-separated lists for each step of the pipeline.

  • find . -type d -print0

    This gets a list of all subdirectories of the current directory.

  • sort -z

    This sorts the directories which as the effect of putting subdirectories after their parent directories.

  • gawk '{a[$0]; sub(/[/][^/]*$/, ""); if ($0 in a) delete a[$0]} END{for (d in a)print d}' RS='\0'

    This puts the list of directories in an associative array. For each directory added, it checks to see if the directories parent is in the array. If it is, it deletes it from the array. Let's look at this one step at a time:

    • a[$0]

      This adds the directory named in the current record as a key in array a. (We don't assign a value to it because we don't need a value.)

    • sub(/[/][^/]*$/, "")

      This removes the last subdirectory from $0 so that $0 now refers to the parent directory.

    • if ($0 in a) delete a[$0]

      If the parent directory is a key in a, then we delete it.

    • END{for (d in a) print d}

      This prints out each directory for which we found no subdirectories. (Because of the way awk's arrays work, the output is in no particular order.)

    • RS='\0'

      This tells awk to expect nul-separated input. In other words, on input, awk uses the nul character as the record separator.

Example

Let's create some directories and then verify that the output lists only those directories that have no subdirectories:

$ mkdir -p a/b/c/x1 a/b/c/x2 a/b/x3 a/x4  $ find . -type d -print0 | sort -z | gawk '{a[$0]; sub(/[/][^/]*$/, ""); if ($0 in a) delete a[$0]} END{for (d in a)print d}' RS='\0'  ./a/x4  ./a/b/x3  ./a/b/c/x1  ./a/b/c/x2  

Variation: printing only the basename of the directory

find . -type d -print0 | sort -z | gawk '{a[$0]; sub(/[/][^/]*$/, ""); if ($0 in a) delete a[$0]} END{for (d in a) {sub(/^.*[/]/,"",d); print d}}' RS='\0'  

To illustrate, let's run this against the same directories as above:

$ find . -type d -print0 | sort -z | gawk '{a[$0]; sub(/[/][^/]*$/, ""); if ($0 in a) delete a[$0]} END{for (d in a) {sub(/^.*[/]/,"",d); print d}}' RS='\0'  x5  x4  x3  x1  x2  


Solution:2

You could do a find-within-a-find e.g.

find . -type d -exec bash -c '    [[ -z $(find "$1" -mindepth 1 -type d -print -quit) ]] && printf "%s\n" "$1"        ' bash {} \;  

If you don't want the leading path components, then

find . -type d -exec bash -c '    [[ -z $(find "$1" -mindepth 1 -type d -print -quit) ]] && printf "%s\n" "${1##*/}"  ' bash {} \;  

I can't help feeling there ought to be a "smart" way to do it using -depth


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »