3

I have a directory tree that I want to search recursively for all files of a certain type (for example all .jpg's), but I only want to list these files if they are at the bottom of a directory tree.

As an example if my tree looks like:

                          home
                          root
                   f1     f2     f3
                   f11    f22    f33
                   f111   f222   f333

and all folders have .jpg's, I only want to list the .jpg's in f111/f222/f333, and so on, and ignore the ones in the higher up folders.

I know that I can use a find command (like find . -name "*.jpg") to get a listing of all .jpg's in the entire tree, but I'm not sure how to limit it to only the bottom most folders without doing something like grep'ing the results to filter for 111/222/333's which isn't really feasible as I have hundreds of folders and I don't know what the last level folders might be named for all of them individually.

2

You can do this in two steps:

find . -type d -links 2 -print0 | xargs -0 -I{} find {} -name \*.jpg

The first command lists all directories without any subdirectories (since each subdirectory adds one to the link count, while an empty directory has two). The second invokes find manually on each of these results.

  • Note that the -links 2 trick won't work with all filesystems (not with btrfs or nilfs2 or ntfs or vfat for instance). – Stéphane Chazelas Nov 5 '15 at 16:45
  • Huh. I had not yet noticed that btrfs didn't do the directory-link-count thing. – Tom Hunt Nov 5 '15 at 16:54
1

A POSIX equivalent of @TomHunt's solution.

find . -type d -links 2 -exec sh -c '
  find "$@" -type f -name "*.jpg"' sh {} +

The -links 2 relies on the property of some file systems that the link count of directories (initially 2 for the entry in their parent and the . entry in themselves) increases by one for every subdir (because of the .. entry in them). Some modern filesystems like btrfs or nilfs whose structures are radically different don't have such a property though.

With zsh, you could do something like:

has_subdirs() {
  local l
  l=($REPLY/*(/DNoN[1]))
  (($#l))
}
dirs_without_subdirs=(**/*(DN^+has_subdirs))
jpgs=($^dirs_without_subdirs/*.jpg(ND.))
(($#jpgs)) && printf '%s\n' $jpgs

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.