Sous Linux ou Windows, comment trouver le chemin de tous les mp3 qui sont les seuls mp3 de ce répertoire?

3

J'ai un dossier de musique et le contenu ressemble à ceci:

La musique
    -Artiste
        -Song1.mp3
        -AlbumA
            -Song2.mp3
            -Song2.m4a
        -AlbumB
            -Song1.mp3
            -Song2.mp3
            -Song3.mp3
        -Album C
            -Song1.mp3
            -Song1.jpg
            -SomeFolder1
                -Song1.mp3
    -ArtisteB
        -Song1.mp3
    -ArtisteC
        -Song1.mp3
        -Song2.mp3
        -Song3.mp3
        -SomeFolder2
            -Song1.jpg
            -Song2.jpg
        -SomeFolder3


Je cherche une sortie comme celle-ci:

ArtistA / Song1.mp3
ArtisteA / AlbumA / Song2.mp3
ArtisteA / AlbumC / Song1.mp3
ArtistA / AlbumC / SomeFolder1 / Song1.mp3
ArtistB / Song1.mp3



Donc, je ne veux pas de répertoires avec un seul fichier, je veux des répertoires avec un seul mp3 - indépendamment des sous-répertoires ou autres fichiers. Je ne veux pas non plus de répertoires vides, ni de répertoires contenant plus d’un mp3.

Je ne suis pas opposé à un script shell pour Linux. Je ne veux pas compiler un programme sur l'un ou l'autre système d'exploitation, cependant.

J'ai essayé: find . -type d -exec sh -c 'set -- "$0"/*.mp3; [ $# -le 1 ]' {} \; -print

mais ça me donne cette sortie:

.
./ArtistB
./Artiste
./ArtistA/AlbumA
./ArtistA/AlbumC
./ArtistA/AlbumC/SomeFolder1
./ArtistC/SomeFolder2
./ArtistC/SomeFolder3

ce qui est proche, mais pas exactement ce que je veux. Il y a deux dossiers vides et aucun nom de fichier.

RalphyZ
la source

Réponses:

3

À première vue, la première raison pour laquelle votre première commande signale les dossiers vides (ceux sans *.mp3fichiers) est ce que vous dites -le 1plutôt que -eq 1. Cela semble indiquer que les répertoires contenant un fichier MP3 ou moins sont répertoriés. Mais changer -le 1pour -eq 1ne fonctionne pas. Il se trouve que le problème est que, dans un répertoire contenant ≥ un fichier MP3, directory_name/*.mp3vous obtenez une liste des noms de fichiers MP3, mais que dans un répertoire ne contenant aucun fichier, il reste littéralement sous la forme directory_name/*.mp3. Cela peut être corrigé en indiquant au shell que les caractères génériques ne correspondant à aucun fichier doivent simplement disparaître de la ligne de commande:

trouver . -type d -exec
        sh -c ' shopt -s nullglob; set - "$ 0" / *. mp3; [$ # -eq 1] '{} \; -impression

(tapé tous comme une seule ligne)

./ArtistA
./ArtistA/AlbumA
./ArtistA/AlbumC
./ArtistA/AlbumC/SomeFolder1
./ArtistB

Mais vous avez le shell qui compte les fichiers, et vous demandez findde faire l’impression - et findne regardez que les répertoires. Pourquoi ne pas laisser le shell imprimer le nom de fichier qu’il voit?

trouver . -type d -exec
        sh -c 'shopt -s nullglob; set - "$ 0" / *. mp3; [$ # -eq 1] && echo "$ 1" '{} \;

(c’est-à-dire s’il ya un fichier, echole nom du premier ( $1)) vous donnera ce que vous voulez.

G-Man
la source
4

Pour Windows utilisant PowerShell, en supposant que vous vous trouviez déjà dans le dossier C: \ YOUR_PATH:

PS C:\YOUR_PATH> (Get-ChildItem -recurse | Where-Object {$_.name -match ".mp3"}) | Group-Object -Property Directory | Where-Object {$_.Count -eq 1} | ForEach-Object { (Get-ChildItem $_.Name -Filter "*.mp3").FullName}

Les sorties:

C:\YOUR_PATH\ArtistA\Song1.mp3
C:\YOUR_PATH\ArtistA\AlbumA\Song2.mp3
C:\YOUR_PATH\ArtistA\AlbumC\Song1.mp3
C:\YOUR_PATH\ArtistA\AlbumC\SomeFolder1/Song1.mp3
C:\YOUR_PATH\ArtistB\Song1.mp3

Décomposer la commande piped:

Récupère tous les fichiers et dossiers sous le répertoire courant

Get-ChildItem -recurse 

Ne correspond qu'aux fichiers en fonction de votre masque

Where-Object {$_.name -match ".mp3"})

Effectuer un regroupement en fonction de la propriété Directory

Group-Object -Property Directory 

Sélectionnez uniquement les répertoires avec 1 élément

Where-Object {$_.Count -eq 1} 

Extrayez le nom complet du fichier correspondant à votre masque de fichier dans chaque répertoire

ForEach-Object { (Get-ChildItem $_.Name -Filter "*.mp3").FullName} 
nvuono
la source
1

Je suis sûr que quelqu'un a un meilleur moyen de le faire, mais cette commande bash fonctionnera sous Linux.

for Folder in $(find . -name "*.mp3" -printf "%h\n")
do
    [[ $(ls -l $Folder/*.mp3 | wc -l) -eq "1" ]] && ls $Folder/*.mp3
done

Ce script effectue un test sur le dossier de chaque fichier .mp3, en se répétant pour chaque correspondance sur le même fichier. Par exemple, il vérifie 3 fois Musique / ArtisteA / AlbumB. Selon le nombre de fichiers mp3 que vous avez, cela peut prendre beaucoup de temps (vous auriez besoin d'une assez grande bibliothèque).

Si oui, vous pouvez le rendre un peu plus compliqué:

for Folder in $(find . -name "*.mp3" -printf "%h\n" | awk '!x[$0]++')
    do
        [[ $(ls -l $Folder/*.mp3 | wc -l) -eq "1" ]] && ls $Folder/*.mp3
    done

Le résultat est identique, mais chaque dossier ne sera vérifié qu'une seule fois, ce qui pourrait vous faire gagner beaucoup de temps. La commande awk supprime tous les noms de dossiers en double.

Une autre note est que les fichiers sont sensibles à la casse sur Linux, donc cela ignorera totalement tout .MP3ou .Mp3fichiers. Si vous voulez qu'il corresponde à ceux-ci, changez les 3 instances de mp3en[mM][pP]3

Omniprésence
la source
protip: pour rechercher des correspondances insensibles à la casse pour les noms de fichiers, vous pouvez utiliser -iname <modèle> au lieu de -name <modèle>.
Webmarc
C'est vrai, mais il a quand même besoin de l'expression rationnelle dans les commandes ls, alors j'ai pensé la garder cohérente.
Omniprésence
0

Voici une réponse:

find Music -name "*.mp3" -exec dirname {} \; |
    sort |
    uniq -c |
        while read count parent
        do
            if [[ $count -eq 1 ]]
            then
                echo "$parent"/*.mp3
            fi
        done
bonh
la source
Essayez de remplacer l'intérieur findavec echo "$parent"/*.mp3.
G-Man