
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, *.mp3
dass Sie -le 1
statt sagen -eq 1
. Das sieht so aus, als würde es Verzeichnisse mit einer oder weniger MP3-Dateien melden. Aber die Änderung -le 1
auf -eq 1
funktioniert nicht. Es stellt sich heraus, dass das Problem darin besteht, dass
directory_name/*.mp3
Sie 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 find
den Ausdruck ausführen – und find
dabei 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 echo
der 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 .MP3
oder .Mp3
-Dateien vollständig ignoriert werden. Wenn Sie möchten, dass diese übereinstimmen, ändern Sie alle 3 Instanzen von mp3
in[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