I want to find and list all directories that contain a file that ends in .grid in a parent directory /work/user/folder1/*/*

find /work/user/folder1/*/*/ '!' -mindepth 1 -maxdepth 1 -name *.grid

The next step I want to do is to find and all the directories missing the file that ends in .grid Basically I want the command to print out missing folders.

How can I do that?


Use find to call find!

find . -type d \( -exec sh -c 'cd "$0"; find . \( -name . -o -prune \) -name "*.grid" | grep -q .' {} \; -o -print \)

This is POSIX-compliant, thanks to this answer:

Also, this will work regardless of spaces, special characters or even newlines in filenames or directory names. :)

(N.B.: If you do have special characters or newlines in your directory names, you should change that final -print primary to whatever action you want to do with the directories, since you won't be able to safely parse the resulting printed list if you just use -print.)

Here is a version with line breaks added in a possibly misguided attempt to aid readability:

find . -type d \
  \( \
    -exec sh -c '
      cd "$0";
      find . \( -name . -o -prune \) -name "*.grid" |
        grep -q .
    ' {} \; \
    -o -print \
  • 1
    I much prefer the line breaks! Thank you.
    – Jim L.
    Jul 9 '19 at 22:53

Step 1.) Create a list of all applicable directories, sort it in alphanumeric order and make sure there are no duplicates. Save the list in a temporary file:

find /work/user/folder1/ [...] -type d | sort | uniq > all_directories.txt

Step 2.) Create a list of all *.grid files.

find /work/user/folder1/ [...] -name *.grid > grid_files.txt

Step 3.) Work through the list of *.grid files, take the directory name of each file, and again make sure there are no duplicates:

while read FILENAME
    echo $(dirname "$FILENAME")
done < grid_files.txt | sort | uniq > dirs_with_gridfiles.txt

Step 4.) Run those two lists together and sort the result again. Now, directories with *.grid files should be listed exactly twice each, and directories with no *.grid files exactly once each. So you can tell uniq to report only the non-repeated lines:

cat all_directories.txt dirs_with_gridfiles.txt | sort | uniq -u > dirs_with_no_gridfile.txt

And there you'll have it.


Here's a two-step solution. Overall, the procedure is:

  1. Find all *.grid files in your path, remove the base filename, and uniq the list. This gives you a list of all directories that contain one or more *.grid files.

$ find /work/user/folder1/*/*/ -type f -name *.grid | sed -e 's-[^/]*$--' | sort | uniq > grid-dirs.txt

  1. Find all directories in your path and use grep to filter out the directories found in step 1. This gives you a list of all directories that don't contain any *.grid files.

$ find /work/user/folder1/*/*/ -type d | grep -Fvxf grid-dirs.txt

For example, suppose:

$ find /work

After step 1, grid-dirs.txt is:


and the step 2 output is:

$ find /work/user/folder1/*/*/ -type d | grep -Fvxf grid-dirs.txt

If you have Bash v4 (or greater) I think you can also do it with one loop looking for all *.grid files, plus one inner loop over all directories to list which directories have the files and which don't. With Bash v4 we use one of its options that is usually disabled.

# 'globstar' enables '**' expansions, which instructs
# the shell to search all and below recursively
shopt -s globstar

# Note that '**' is implicitly recursive, no need to specify
# depth. If you do want to specify precise depth, then use a
# '*/' for each wanted depth, like '*/*/*/' for 3 depths
for f in /work/user/folder1/**/*.grid; do
    f="${f##*/}"  # strip directory from found name
    printf '\nNow looking for: %s\n' "$f"
    # A simple '**/' returns directories only
    for d in /work/user/folder1/**/; do
        # simple existence test
        [ -e "${d}${f}" ] && \
            printf '    present in %s\n' "$d" || \
            printf 'not present in %s\n' "$d"

Unfortunately the ** syntax is not POSIX.

However, if you know the precise depth you need to search, just take the shopt command away and turn each **/ into the precise depth expressed as */ repetitions, and it should work well on POSIX shells too.

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.