Here's a relatively simple solution that runs find
only once, stores its output in a temporary file and then splits out and collates the results for the two extensions.
tmp=$(mktemp)
find . -name '*.ext1' -o -name '*.ext2' | sort >"$tmp"
comm -12 <(<tmp sed -n 's!/[^/]*\.ext1$!!p' | sort) \
<(<tmp sed -n 's!/[^/]*\.ext2$!!p' | sort)
rm "$tmp"
Another method is to run find
to iterate over the directories and uses an auxiliary program to check whether a pattern has matches. Note that the existence check works in this case, but you need something more complicated if your search pattern doesn't match itself and is a possible file name.
find . -type d -exec sh -c '{ set "$0"/*.ext1; [ -e "$1" ]; } &&
{ set "$0"/*.ext2; [ -e "$1" ]; }' {} \;
Here's a zsh solution that operates like this last command:
echo **/*(/e\''set -- $REPLY/*.ext1(N[1]) $REPLY/*.ext2(N[1]); ((#==2))'\')
Here's another zsh solution that looks for *.ext1
and selects only the directories that also have *.ext2
:
echo ./**/*.ext1(e\''REPLY=${REPLY:h}; set -- $REPLY/*.ext2(N); ((#))'\')
Here's a partial Perl solution; due to the vicissitudes of Perl's globbing, it won't work if directory names contain spaces (there are ways to fix this, but I can't find one that's remotely elegant).
perl -l -MFile::Find -e \
'find {no_chdir => 1,
wanted => sub {<$_/*.ext1> and <$_/*.ext2> and print}}, "."'
+1 for making me laugh when your self-proclaimed "hack" started to unravel :-) . Well played, sir. – Daniel Andersson – 2012-05-12T15:03:01.510