Wie finde ich unter Linux oder Windows den Pfad für alle MP3s, die die einzigen MP3s in diesem Verzeichnis sind?

Wie finde ich unter Linux oder Windows den Pfad für alle MP3s, die die einzigen MP3s in diesem Verzeichnis sind?

Ich habe einen Musikordner und der Inhalt sieht folgendermaßen aus:

Musik
    -KünstlerA
        -Song1.mp3
        -AlbumA
            -Song2.mp3
            -Song2.m4a
        -AlbumB
            -Song1.mp3
            -Song2.mp3
            -Song3.mp3
        -AlbumC
            -Song1.mp3
            -Lied1.jpg
            -Ein Ordner1
                -Song1.mp3
    -KünstlerB
        -Song1.mp3
    -KünstlerC
        -Song1.mp3
        -Song2.mp3
        -Song3.mp3
        -Ein Ordner2
            -Lied1.jpg
            -Song2.jpg
        -Ein Ordner3


Ich suche nach einer Ausgabe wie dieser:

KünstlerA/Song1.mp3
KünstlerA/AlbumA/Song2.mp3
KünstlerA/AlbumC/Song1.mp3
KünstlerA/AlbumC/Ein Ordner1/Song1.mp3
KünstlerB/Song1.mp3



Ich möchte also keine Verzeichnisse mit nur einer Datei, sondern Verzeichnisse mit nur einer MP3 - unabhängig von Unterverzeichnissen oder anderen Dateien. Ich möchte auch keine leeren Verzeichnisse oder Verzeichnisse, die mehr als eine MP3 enthalten.

Ich habe nichts gegen ein Shell-Skript für Linux. Ich möchte jedoch auf keinem der beiden Betriebssysteme ein Programm kompilieren.

Ich habe Folgendes versucht:, find . -type d -exec sh -c 'set -- "$0"/*.mp3; [ $# -le 1 ]' {} \; -print

aber das gibt diese Ausgabe zurück:

.
./KünstlerB
./KünstlerA
./KünstlerA/AlbumA
./KünstlerA/AlbumC
./KünstlerA/AlbumC/Ein Ordner1
./KünstlerC/Ein Ordner2
./KünstlerC/Ein Ordner3

das kommt dem nahe, ist aber nicht genau das, was ich will. Es gibt zwei leere Ordner und keine Dateinamen.

Antwort1

Auf den ersten Blick ist der Grund, warum Ihr erster Befehl die leeren Ordner (die ohne Dateien) meldet, *.mp3dass Sie -le 1statt sagen -eq 1. Das sieht so aus, als würde es Verzeichnisse mit einer oder weniger MP3-Dateien melden. Aber die Änderung -le 1auf -eq 1funktioniert nicht. Es stellt sich heraus, dass das Problem darin besteht, dass directory_name/*.mp3Sie in einem Verzeichnis mit ≥ einer MP3-Datei eine Liste der MP3-Dateinamen erhalten, in einem Verzeichnis ohne jedoch buchstäblich als bleibt directory_name/*.mp3. Das kann behoben werden, indem Sie der Shell sagen, dass Platzhalter, die mit keiner Datei übereinstimmen, einfach aus der Befehlszeile verschwinden sollen:

suchen . -type d -exec
        sh -c 'shopt -s Nullglob;setze -- "$0"/*.mp3; [ $# -eq 1 ]' {} \; -print

(alles in einer Zeile eingegeben) ergibt

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

Aber Sie haben die Shell dazu gebracht, die Dateien zu zählen, und Sie möchten findden Ausdruck ausführen – und finddabei werden nur Verzeichnisse durchsucht. Warum lassen Sie die Shell nicht einfach den Dateinamen drucken, den sie sieht?

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

(wenn also nur eine Datei vorhanden ist, wird echoder Name der ersten ( $1)) Ihnen das gewünschte Ergebnis liefern.

Antwort2

Für Windows mit PowerShell, vorausgesetzt, Sie befinden sich bereits im Ordner C:\IHR_PFAD:

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}

Ausgaben:

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

Aufschlüsselung des weitergeleiteten Befehls:

Alle Dateien und Ordner im aktuellen Verzeichnis abrufen

Get-ChildItem -recurse 

Ordnen Sie nur die Dateien basierend auf Ihrer Maske zu.

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

Gruppierung basierend auf Verzeichniseigenschaften durchführen

Group-Object -Property Directory 

Wählen Sie nur die Verzeichnisse mit 1 Eintrag aus

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

Extrahieren Sie den vollständigen Namen der Datei, die Ihrer Dateimaske in jedem Verzeichnis entspricht

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

Antwort3

Ich bin sicher, dass jemand eine bessere Möglichkeit hat, dies zu tun, aber dieser Bash-Befehl funktioniert unter Linux.

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

Dieses Skript führt einen Test für den Ordner jeder einzelnen MP3-Datei durch und wiederholt sich für jede Übereinstimmung mit derselben Datei. Beispielsweise prüft es Musik/InterpretA/AlbumB dreimal. Je nachdem, wie viele MP3-Dateien Sie haben, kann dies lange dauern (Sie benötigen eine ziemlich große Bibliothek).

Wenn ja, können Sie es etwas komplizierter machen:

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

Das Ergebnis ist das gleiche, aber jeder Ordner wird nur einmal überprüft, was viel Zeit sparen kann. Der Befehl awk entfernt alle doppelten Ordnernamen.

Ein weiterer Hinweis ist, dass Dateien unter Linux zwischen Groß- und Kleinschreibung unterscheiden, sodass alle .MP3oder .Mp3-Dateien vollständig ignoriert werden. Wenn Sie möchten, dass diese übereinstimmen, ändern Sie alle 3 Instanzen von mp3in[mM][pP]3

Antwort4

Hier ist eine Antwort:

find Music -name "*.mp3" -exec dirname {} \; |
    sort |
    uniq -c |
        while read count parent
        do
            if [[ $count -eq 1 ]]
            then
                echo "$parent"/*.mp3
            fi
        done

verwandte Informationen