Find Command – How to Find All Files in Directories Named foo

directoryfind

I would like to find all files which reside in any directory foo. For example, consider the following files:

foo/w
a/foo/x
a/b/foo/y
a/b/c/foo/z
a/b/c/foo/bar/n

I would like to find the files w,x,y,z and get them listed as above, i.e., with their relative paths. My attempt was something like

$ find . -path '**/foo' -type f

which doesn't work. The search should not include file n, i.e., only files whose parent directory is named foo we are interested in.

Best Answer

Not using find, but globbing in the zsh shell:

$ printf '%s\n' **/foo/*(^/)
a/b/c/foo/z
a/b/foo/y
a/foo/x
foo/w

This uses the ** glob, which matches "recursively" down into directories, to match any directory named foo in the current directory or below, and then *(^/) to match any file in those foo directories. The (^/) at the end of that is a glob qualifier that restricts the pattern from matching directories (use (.) to match regular files only, or (-.) to also include symbolic links to regular files).

In bash:

shopt -s globstar nullglob

for pathname in **/foo/*; do
    if [[ ! -d "$pathname" ]] || [[ -h "$pathname" ]]; then
        printf '%s\n' "$pathname"
    fi
done

I set the nullglob option in bash to remove the pattern completely in case it does not match anything. The globstar shell option enables the use of ** in bash (this is enabled by default in zsh).

Since bash does not have the glob qualifiers of zsh, I'm looping over the pathnames that the pattern matches and test each match to make sure it's not a directory before printing it. Change the "! -d || -h" test to a "-f && ! -h" test to instead pick out only regular files, or just a single "-f" test to also print symbolic links to regular files.

Related Question