1

I have a directory with contents like the below example. I want to search subdir1, subdir2 & subdir3 for files testA/*a.txt, testB/*b.txt & testC/*c.txt. I want a list of all subdirectories only where all 3 exist.

In this example, that would be only subdir1 & subdir2.

subdir1/
subdir1/testA/
subdir1/testA/subdir1-a.txt
subdir1/testB/
subdir1/testB/subdir1-b.txt
subdir1/testC/
subdir1/testC/subdir1-c.txt
subdir2/
subdir2/testA
subdir2/testA/subdir2-a.txt
subdir2/testB
subdir2/testB/subdir2-b.txt
subdir2/testC/
subdir2/testC/subdir2-c.txt
subdir3/
subdir3/testA
subdir3/testA/subdir3-a.txt
subdir3/testB
subdir3/testB/subdir3-b.txt
subdir3/testC/
subdir3/testZ
subdir3/testZ/subdir3-z.txt

I want to then get the output in alphabetical order, e.g.:

subdir1
subdir2

...and then get only the unique ones

(The need for this exists because I'm using find at the moment, and the directories are listed out of order. The subdirs also have longer/more complex names.)

How can I do this?

I've gotten so far as to list one type of file, e.g. the *-a.txt file, like this:

find . -wholename "**/testA/*a.txt

Apologies if the answer already exists somewhere, I looked but couldn't find one. Any advice would be much appreciated.

1 Answer 1

0

How about something like this:

$ shopt -s nullglob
$ for dir in */; do 
    f1=( "$dir"/testA/*a.txt ) 
    f2=( "$dir"/testB/*b.txt )
    f3=( "$dir"/testC/*c.txt )
    if [[ "${#f1[@]}" -gt 0  && "${#f2[@]}" -gt 0 && "${#f3[@]}" -gt 0 ]]; then 
        printf '%s\n' "$dir" 
    fi
done
subdir1/
subdir2/

First, you turn on bash's nullglob option to make sure that globs that don't match any files expand to the empty string. Next, you iterate over all subdirectories in the current directory, and for each of them, you save the result of expanding the corresponding glob to an array. Then, if all three arrays have at least one element, print the name of the subdir.

You could do the same basic thing with find too:

$ for dir in */; do 
    if [[ $(find "$dir" -wholename "*/testA/*a.txt" | wc -l) -gt 0 &&
          $(find "$dir" -wholename "*/testB/*b.txt" | wc -l) -gt 0 &&
          $(find "$dir" -wholename "*/testC/*c.txt" | wc -l) -gt 0 ]]; then 
        printf '%s\n' "$dir" 
    fi
done
subdir1/
subdir2/
3
  • Thanks! This helps, then how would I sort the output (list of subdirs with all these files) alphabetically, and find the unique list of all subdirs as determined by the first 7 characters? i.e. if I have subdir1a and subdir1b, I want only subdir1 as the output in the final list
    – abra
    Commented Dec 7, 2022 at 10:29
  • @abra that... is a very different question. A simple way, assuming your directory names are simple ASCII and not UTF8 and that you want to sort according to your system's locale settings, would be to pass the output of either of the two commands above through | cut -c -7 | sort | uniq.
    – terdon
    Commented Dec 7, 2022 at 11:51
  • thanks, it worked! It was included in my main question, I wasn't sure how to integrate it. Marked your solution as accepted. Thanks again!
    – abra
    Commented Dec 7, 2022 at 12:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .