11

given the following structure:

oz123@debian:~/ $ tree .
.
├── a
│   ├── a1
│   ├── a2
│   └── a3
├── a1
│   ├── a11
│   ├── a12
│   └── a31
├── b
│   └── b1
│       ├── b11
│       │   └── b21
│       │       └── b31
│       ├── b12
│       └── b3
└── c

16 directories, 0 files

How do I find all the end nodes?

I found the following solutions which seems to be good, but I have to proof that there is not test case which will fail it.

The help page of the -links states:

You can also search for files that have a certain number of links, with ‘-links’. Directories normally have at least two hard links; their . entry is the second one. If they have subdirectories, each of those also has a hard link called .. to its parent directory. The . and .. directory entries are not normally searched unless they are mentioned on the find command line.

possible solution:

oz123@debian:~/ $ find .  -type d  -links 2
./a/a2
./a/a3
./a/a1
./c
./a1/a31
./a1/a11
./a1/a12
./b/b1/b12
./b/b1/b3
./b/b1/b11/b21/b31
  • Can anyone provide a better solution (without using pipes and sed, this has be performant ...)
  • Will it work on any filesystem?
  • 3
    You won't find more performant than the -links 2 trick. It won't work on btrfs. – Stéphane Chazelas Aug 12 '13 at 8:46
3

As an addition to your own solution with -links, I want to just add that it will not work on filesystems that do not follow the Unix directory-link convention. From man find on option -noleaf these are at least CD-ROM, MS-DOS filesystems and AFS volume mount points.

For a reference, this question was already discussed with different solutions which are indeed slower and usually resort to piping to sed / awk and similar.

3

There is a bit more obvious option -empty:

find . -type d -empty

upd. Ok, you're right this way won't work with files in dirs.

So here it is a fixed file system undependable version:

find dtest/ -type d -exec sh -c "if [ \$(find {} -maxdepth 1 -type d | wc -l) -eq 1 ]; then echo {} ; fi" \;
  • 2
    If I understand the question, end directories might contain files. This wouldn't print those directories as they wouldn't be "empty"... – Stefan Aug 12 '13 at 8:39
  • @rush, subdirs can be empty or not – Oz123 Aug 12 '13 at 8:51
  • @Oz123 please check my update, it should be fast enough, but a bit slower comparing to your way. – rush Aug 12 '13 at 12:29
  • @rush, thanks, but I really need to avoid the pipes, they can make things slower. – Oz123 Aug 12 '13 at 13:50
1

find . -type d -links 2 works on most filesystems, but not all. I don't think there's a way to know other than knowing which filesystem types have the property that directories contain a link to themselves. GNU find detects this dynamically (if it prints something out about “Automatically turning on find's -noleaf option”, you know your filesystem doesn't have this property). Most common filesystem types are ok, but not FAT or btrfs.

If you want to be sure, you'll have to test each directory. One way to do this is to invoke find again for each subdirectory.

find . -type d ! -exec sh -c '
   find "$1/." ! -name . -type d -prune | grep -q "^"' sh {} \; -print

(with GNU find, you can replace -prune with -print -quit to make it a bit more efficient).

Another way is to post-process the output of find. With find -depth, a leaf directory is one that does not follow a subdirectory of itself.

find . -depth -type d -print0 |
awk -v RS='\0' '
    substr(previous, 1, length($0) + 1) != $0 "/"
    { previous = $0 }
'
0

Try the following solution (should be Linux, Unix and OS X compatible):

find . -type d -execdir sh -c 'test -z "$(find "{}" -mindepth 1 -type d)" && echo $PWD/{}' ';'

It's similar approach to rush solution, but without any pipes.

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.