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

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

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

Не беспокойтесь о загрузке обложки (это забавный проект для меня завтра). Меня волнует только великолепный взрыв в примере с обратным выводом find.

58
задан 10 October 2012 в 13:39

4 ответа

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

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

Ура.

Примечания:

  • commаргументы следующие:

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

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

  • dirname удобный инструмент для получения dir файла, не обращаясь к sed (и др.).

  • find и comm оба немного грязны с их выводом. Финал sed есть ли для чистки вещей, таким образом, с Вами оставляют Artist/Album. Это может или не может быть желательно для Вас.

12
ответ дан 10 October 2012 в 13:39

Случай 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. . должен быть экранирован, иначе это означает, что он соответствует любому символу. ^ отмечает начало строки, $ ее конец

Другие примеры шаблонов поиска для 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, но НЕ Cover.jpg (чувствительность к регистру не отключена)
  • egrep -iq "^(cover|front)\.jpg$": например, соответствует front.jpg, Cover.JPG, но не Cover.PNG

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

0
ответ дан 10 October 2012 в 13:39

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

$ 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, 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

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

Есть несколько случаев с разбитым углом, например, что по умолчанию * расширяется, если ничего не соответствует. Это можно решить с помощью Баша set -o nullglob.

0
ответ дан 10 October 2012 в 13:39
ls --color=never */*.txt | sed 's|/.*||' | sort -u -n > withtxt.txt
ls --color=never -d * | sort -u -n > all.txt
diff all.txt withtxt.txt

покажет все каталоги, которые не имеют txt файлов в них.

0
ответ дан 17 October 2019 в 14:40

Другие вопросы по тегам:

Похожие вопросы: