3

Hy, я получил большое дерево каталогов. Я хочу выяснить все каталоги, которые содержат файл, имя которого заканчивается на «.ext1» и файл, имя которого заканчивается на «.ext2».

Как это возможно? Я думал об использовании двух находок, один для ".ext1" и один для ".ext2", но затем мне нужно найти пересечение, как это можно сделать?

Спасибо!

4 ответа4

5

Вот относительно простое решение, которое запускает find только один раз, сохраняет свои выходные данные во временном файле, а затем разбивает их на части и объединяет результаты для двух расширений.

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"

Другой метод - запустить find для итерации по каталогам и использовать вспомогательную программу для проверки соответствия шаблона. Обратите внимание, что проверка существования в этом случае работает, но вам нужно нечто более сложное, если ваш шаблон поиска не соответствует самому себе и является возможным именем файла.

find . -type d -exec sh -c '{ set "$0"/*.ext1; [ -e "$1" ]; } &&
                            { set "$0"/*.ext2; [ -e "$1" ]; }' {} \;

Вот решение zsh, которое работает как эта последняя команда:

echo **/*(/e\''set -- $REPLY/*.ext1(N[1]) $REPLY/*.ext2(N[1]); ((#==2))'\')

Вот еще одно решение zsh, которое ищет *.ext1 и выбирает только каталоги, которые также имеют *.ext2:

echo ./**/*.ext1(e\''REPLY=${REPLY:h}; set -- $REPLY/*.ext2(N); ((#))'\')

Вот частичное решение Perl; из-за превратностей глобализации Perl, он не будет работать, если имена каталогов содержат пробелы (есть способы исправить это, но я не могу найти тот, который удаленно элегантен).

perl -l -MFile::Find -e \
  'find {no_chdir => 1,
         wanted => sub {<$_/*.ext1> and <$_/*.ext2> and print}}, "."'
2

Если вы знаете точное имя каждого файла:

find start_dir -type d -exec test -e {}/file.ext1 -a -e {}/file.ext2 \; -print

Исключительно уродливый хак будет работать, если все, что вы знаете, это расширения:

find start_dir -type d -execdir bash -c 'shopt -s nullglob; eval '\''test -n "$(echo '{}'/*.ext1)" -a -n "$(echo '{}'/*.ext2)"'\''' \; -print

Это также может быть довольно медленным, если у вас есть много каталогов для поиска.

1

Если запустить из заголовка дерева каталогов, это похоже на работу

find $(find . -name "*.ext1" -printf %h\\n) -name "*.ext2" -printf %h\\n
0

Должно быть лучшее решение, но:

find -type d | while read i ; do [ `ls "$i" -1 | grep -oe '.ext1$\|.ext2$' | sort -u | wc -l` = 2 ] && echo $i ; done

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .