Oui, je trie ma musique. J'ai tout arrangé magnifiquement dans le mantra suivant: /Artist/Album/Track - Artist - Title.ext
et s'il en existe un, la couverture est assise dans /Artist/Album/cover.(jpg|png)
.
Je veux parcourir tous les répertoires de deuxième niveau et trouver ceux qui n'ont pas de couverture. Au deuxième niveau, je veux dire que je me fiche de savoir si /Britney Spears/
n’a pas de cover.jpg, mais je me soucierais de si /Britney Spears/In The Zone/
n'en avait pas.
Ne vous inquiétez pas du téléchargement de la couverture (c'est un projet amusant pour moi demain). Je ne me soucie que de la glorieuse bash-fuiness d'un exemple inverse-ish find
name__.
Simple, ça se passe. Ce qui suit obtient une liste de répertoires avec la couverture et la compare à une liste de tous les répertoires de second niveau. Les lignes qui apparaissent dans les deux "fichiers" sont supprimées, ce qui laisse une liste de répertoires à couvrir.
comm -3 \
<(find ~/Music/ -iname 'cover.*' -printf '%h\n' | sort -u) \
<(find ~/Music/ -maxdepth 2 -mindepth 2 -type d | sort) \
| sed 's/^.*Music\///'
Hourra.
Remarques:
Les arguments de comm
sont les suivants:
-1
supprime les lignes uniques à file1-2
supprime les lignes uniques à file2-3
supprime les lignes apparaissant dans les deux fichierscomm
ne prend que des fichiers, d’où la méthode de saisie kooky <(...)
. Cela dirige le contenu via un fichier réel [temporaire].
comm
a besoin d'une entrée triée ou cela ne fonctionne pas et find
ne garantit en aucun cas une commande. Il doit également être unique. La première opération find
pourrait trouver plusieurs fichiers pour cover.*
, de sorte qu'il pourrait y avoir des entrées en double. sort -u
les remue rapidement à un. La deuxième découverte sera toujours unique.
dirname
est un outil pratique pour obtenir le répertoire d'un fichier sans avoir recours à sed
(et al).
find
et comm
sont un peu compliqués avec leur sortie. Le nom final sed
est là pour nettoyer les choses afin que vous restiez avec Artist/Album
. Cela peut ou peut ne pas être souhaitable pour vous.
Utilisez find
avec test -e your_file
pour vérifier si un fichier existe. Par exemple, vous recherchez des répertoires ne contenant pas cover.jpg
:
find base_dir -mindepth 2 -maxdepth 2 -type d '!' -exec test -e "{}/cover.jpg" ';' -print
C'est sensible à la casse cependant.
Vous n'êtes pas sûr du cas et l'extension peut être jPg
name__, png
name __...
find base_dir -mindepth 2 -maxdepth 2 -type d '!' -exec sh -c 'ls -1 "{}"|egrep -i -q "^cover\.(jpg|png)$"' ';' -print
sh
pour chaque répertoire, car la tuyauterie n'est pas possible avec find
name__ls -1 "{}"
sort seulement les noms de fichiers du répertoire que find
est en train de parcouriregrep
(au lieu de grep
name__) utilise des expressions régulières étendues; -i
rend la recherche insensible à la casse, -q
permet d'omettre tout résultat"^cover\.(jpg|png)$"
est le modèle de recherche. Dans cet exemple, cela correspond par exemple à cOver.png
, Cover.JPG
ou cover.png
. Le .
doit être échappé, sinon cela signifie qu'il correspond à à tout caractère . ^
marque le début de la ligne, $
sa finAutres exemples de motifs de recherche pour egrep :
Remplacez la partie egrep -i -q "^cover\.(jpg|png)$"
par:
egrep -i -q "cover\.(jpg|png)$"
: correspond également à cd_cover.png
, album_cover.JPG
...egrep -q "^cover\.(jpg|png)$"
: correspond à cover.png
, cover.jpg
, mais PAS à Cover.jpg
(la sensibilité à la casse n'est pas désactivée)egrep -iq "^(cover|front)\.jpg$"
: correspond par exemple à front.jpg
, Cover.JPG
mais pas Cover.PNG
Pour plus d'informations à ce sujet, consultez Expressions régulières .
C'est beaucoup plus agréable à résoudre avec Globbing qu'avec 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
Supposons maintenant que vous n’ayez pas de fichiers parasites dans cette belle structure. Le répertoire actuel ne contient que des sous-répertoires d'artistes, et ceux-ci ne contiennent que des sous-répertoires d'albums. Ensuite, nous pouvons faire quelque chose comme ceci:
$ diff <(for x in */*/cover.jpg; do echo "$(dirname "$x")" ; done) <(printf "%s\n" */*)
La syntaxe <(...)
est Substitution du processus Bash: elle vous permet d’utiliser une commande à la place d’un argument de fichier. Il vous permet de traiter la sortie d'une commande en tant que fichier. Ainsi, nous pouvons exécuter deux programmes et prendre leurs diff, sans enregistrer leur sortie dans des fichiers temporaires. Le programme diff
pense qu'il fonctionne avec deux fichiers, mais en fait, il lit deux canaux.
La commande qui génère l’entrée droite de diff
name__, printf "%s\n" */*
répertorie uniquement les répertoires de l’album. La commande de gauche effectue une itération dans les chemins *.cover
et affiche leurs noms de répertoire.
Essai:
$ 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, les répertoires a/b
et foo/bar
n'ont pas de cover.jpg
.
Il y a des cas de coins cassés, comme par défaut *
s’agrandit à lui-même s’il ne correspond à rien. Ce problème peut être résolu avec le set -o nullglob
de Bash.