Найти каталоги, которые НЕ содержат файл

48

Да, я разбираюсь в своей музыке. У меня все прекрасно составлено в следующей мантре: /Artist/Album/Track - Artist - Title.ext , и если таковая существует, обложка находится в /Artist/Album/cover.(jpg|png) .

Я хочу просмотреть все каталоги второго уровня и найти те, у которых нет обложки. На втором уровне, я имею в виду, мне все равно, если /Britney Spears/ не имеет cover.jpg, но мне было бы интересно, если /Britney Spears/In The Zone/ не было.

Не беспокойтесь о загрузке обложки (для меня это забавный проект завтра). Я только забочусь о славном bash-fuiness о примере inverse-ish find .

    
задан Oli 06.10.2012 в 01:27
источник

3 ответа

11

Простой, это выясняется. Следующее получает список каталогов с обложкой и сравнивает это со списком всех каталогов второго уровня. Линии, которые отображаются в обоих файлах, подавляются, оставляя список каталогов, которые нуждаются в обложках.

comm -3 \
    <(find ~/Music/ -iname 'cover.*' -printf '%h\n' | sort -u) \
    <(find ~/Music/ -maxdepth 2 -mindepth 2 -type d | sort) \
| sed 's/^.*Music\///'

Hooray.

Примечания:

  • % аргументов co_de%:

    • comm подавляет строки, уникальные для file1
    • -1 подавляет строки, уникальные для файла2
    • -2 подавляет строки, которые появляются в обоих файлах
  • -3 принимает только файлы, следовательно, метод ввода kooky comm . Это передает содержимое через настоящий [временный] файл.

  • <(...) требует отсортированного ввода или не работает, а comm отнюдь не гарантирует заказ. Он также должен быть уникальным. Первая операция find могла найти несколько файлов для find , чтобы могли быть дубликаты записей. cover.* быстро обрезает их до одного. Вторая находка всегда будет уникальной.

  • sort -u - удобный инструмент для получения файла dir без обращения к dirname (и др.).

  • sed и find являются немного беспорядочными с их выходом. Окончательный comm должен очистить вещи, поэтому вы остаетесь с sed . Это может быть или не быть желательным для вас.

ответ дан Oli 06.10.2012 в 01:39
источник
67

Случай 1: вы знаете точное имя файла для поиска

Используйте find с test -e your_file , чтобы проверить, существует ли файл. Например, вы ищете каталоги, у которых в них нет cover.jpg :

find base_dir -mindepth 2 -maxdepth 2 -type d '!' -exec test -e "{}/cover.jpg" ';' -print

Это чувствительный к регистру факт.

Случай 2: вы хотите быть более гибким

Вы не уверены в этом случае, и расширение может быть jPg , png ...

find base_dir -mindepth 2 -maxdepth 2 -type d '!' -exec sh -c 'ls -1 "{}"|egrep -i -q "^cover\.(jpg|png)$"' ';' -print

Объяснение:

  • Вам нужно создать оболочку sh для каждой директории, поскольку невозможно использовать трубопровод при использовании find
  • ls -1 "{}" выводит только имена файлов каталога find в настоящее время перемещается
  • egrep (вместо grep ) использует расширенные регулярные выражения; -i делает регистр нечувствительным к регистру, -q заставляет его опускать любой вывод
  • "^cover\.(jpg|png)$" - шаблон поиска. В этом примере оно соответствует, например, cOver.png , Cover.JPG или cover.png . . должно быть экранировано, иначе это означает, что оно соответствует символу any . ^ обозначает начало строки, $ ее конец

Другие примеры шаблонов поиска для egrep :

Замените egrep -i -q "^cover\.(jpg|png)$" с помощью:

  • egrep -i -q "cover\.(jpg|png)$" : Также соответствует cd_cover.png , album_cover.JPG ...
  • egrep -q "^cover\.(jpg|png)$" : соответствует cover.png , cover.jpg , но NOT Cover.jpg (чувствительность к регистру не отключена)
  • egrep -iq "^(cover|front)\.jpg$" : соответствует, например. front.jpg , Cover.JPG , но not Cover.PNG

Для получения дополнительной информации об этом, Регулярные выражения .

    
ответ дан phoibos 06.10.2012 в 01:55
7

Это гораздо приятнее решить с помощью globbing, чем с помощью find.

$ cd ... # to the directory one level above the album/artist structure

$ echo */*/*.cover   # lists all the covers

$ printf "%s\n" */*/*.cover # lists all the covers, one per line

Теперь предположим, что у вас нет бродячих файлов в этой красивой структуре. Текущий каталог содержит только подкаталоги исполнителя, и они содержат только подкаталоги альбома. Тогда мы можем сделать что-то вроде этого:

$ diff  <(for x in */*/cover.jpg; do echo "$(dirname "$x")" ; done) <(printf "%s\n" */*)

Синтаксис <(...) - это замена Bash-процесса: он позволяет вам использовать команду вместо аргумента файла. Он позволяет обрабатывать вывод команды в виде файла. Таким образом, мы можем запускать две программы и использовать их diff, не сохраняя их вывод во временных файлах. Программа diff считает, что она работает с двумя файлами, но на самом деле это чтение из двух труб.

Команда, которая производит ввод правой руки на diff , printf "%s\n" */* , просто перечисляет каталоги альбомов. Левая команда выполняет итерацию через пути *.cover и печатает имена своих каталогов.

Тестирование:

$ find .   # let's see what we have here
.
./a
./a/b
./foo
./foo/bar
./foo/baz
./foo/baz/cover.jpg

$ diff  <(for x in */*/cover.jpg; do echo "$(dirname "$x")" ; done) <(printf "%s\n" */*)
0a1,2
> a/b
> foo/bar

Aha, каталоги a/b и foo/bar не имеют cover.jpg .

Есть некоторые разбитые угловые случаи, например, по умолчанию * расширяется до самого себя, если оно ничего не соответствует. Это можно решить с помощью% s__%% Bash.

    
ответ дан Anon 06.10.2012 в 06:20