This is much nicer to solve with globbing than with 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
Now suppose you have no stray files in this nice structure. The current directory contains only artist subdirectories, and those contain only album subdirectories. Then we can do something like this:
$ diff <(for x in */*/cover.jpg; do echo "$(dirname "$x")" ; done) <(printf "%s\n" */*)
The <(...)
syntax is Bash process substitution: it lets you use a command in place of a file argument. It lets you treat the output of a command as a file. So we can run two programs, and take their diff, without saving their output in temporary files. The diff
program thinks it is working with two files, but in fact it's reading from two pipes.
The command that produces the right hand input to diff
, printf "%s\n" */*
, just lists the album directories. The left hand command iterates through the *.cover
paths and prints their directory names.
Test run:
$ 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, the a/b
and foo/bar
directories have no cover.jpg
.
There are some broken corner cases, like that by default *
expands to itself if it matches nothing. This can be addressed with Bash's set -o nullglob
.