Да, я перебираю свою музыку. У меня все прекрасно устроено в следующей мантре: /Artist/Album/Track - Artist - Title.ext
, и если она существует, обложка находится в /Artist/Album/cover.(jpg|png)
.
Я хочу просмотреть все каталоги второго уровня и найти те, которые не имеют обложки. Под вторым уровнем я имею в виду, что мне все равно, если у /Britney Spears/
нет cover.jpg, но мне было бы интересно, если бы у /Britney Spears/In The Zone/
его не было.
Не беспокойтесь о загрузке обложки (это забавный проект для меня завтра). Меня волнует только великолепный взрыв в примере с обратным выводом find
.
Простой, это выясняется. Следующее получает список каталогов с покрытием и сравнивает это со списком всех каталогов второго уровня. Строки, которые появляются в обоих "файлах", подавлены, оставив список каталогов той потребностью покрытия.
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
. Это может или не может быть желательно для Вас.
Используйте find
с test -e your_file
, чтобы проверить, существует ли файл. Например, вы ищете каталоги, в которых нет cover.jpg
:
find base_dir -mindepth 2 -maxdepth 2 -type d '!' -exec test -e "{}/cover.jpg" ';' -print
Это чувствительно к регистру.
Вы не уверены в случае, и расширение может быть 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
Для получения дополнительной информации об этом см. Регулярные выражения .
Это гораздо приятнее решать с помощью шатания, чем с помощью поиска.
$ 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
.
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 файлов в них.