En Linux o Windows, ¿cómo encuentro la ruta de todos los mp3 que son los únicos mp3 en ese directorio?

En Linux o Windows, ¿cómo encuentro la ruta de todos los mp3 que son los únicos mp3 en ese directorio?

Tengo una carpeta de Música y el contenido se ve así:

Música
    -ArtistaA
        -Canción1.mp3
        -ÁlbumA
            -Canción2.mp3
            -Canción2.m4a
        -ÁlbumB
            -Canción1.mp3
            -Canción2.mp3
            -Canción3.mp3
        -ÁlbumC
            -Canción1.mp3
            -Canción1.jpg
            -AlgunaCarpeta1
                -Canción1.mp3
    -ArtistaB
        -Canción1.mp3
    -ArtistaC
        -Canción1.mp3
        -Canción2.mp3
        -Canción3.mp3
        -AlgunaCarpeta2
            -Canción1.jpg
            -Canción2.jpg
        -AlgunaCarpeta3


Estoy buscando un resultado como este:

ArtistaA/Canción1.mp3
ArtistaA/ÁlbumA/Canción2.mp3
ArtistaA/ÁlbumC/Canción1.mp3
ArtistaA/ÁlbumC/AlgunaCarpeta1/Canción1.mp3
ArtistaB/Canción1.mp3



Por lo tanto, no quiero directorios con un solo archivo, quiero directorios con un solo mp3, independientemente de los subdirectorios u otros archivos. Tampoco quiero directorios vacíos o directorios que contengan más de un mp3.

No me opongo a un script de shell para Linux. Sin embargo, no quiero compilar un programa en ninguno de los sistemas operativos.

Lo he intentado: find . -type d -exec sh -c 'set -- "$0"/*.mp3; [ $# -le 1 ]' {} \; -print

pero eso me da este resultado:

.
./ArtistaB
./ArtistaA
./ArtistaA/ÁlbumA
./ArtistaA/ÁlbumC
./ArtistaA/ÁlbumC/AlgunaCarpeta1
./ArtistaC/AlgunaCarpeta2
./ArtistaC/AlgunaCarpeta3

lo cual está cerca, pero no es exactamente lo que quiero. Hay dos carpetas vacías y ningún nombre de archivo.

Respuesta1

A primera vista, la razón por la que su primer comando informa las carpetas vacías (aquellas sin *.mp3archivos) es que está diciendo -le 1en lugar de -eq 1. Parece que informará directorios con un archivo MP3 o menos. Pero cambiar -le 1a -eq 1no funciona. Resulta que el problema es que, en un directorio con ≥ un archivo MP3, directory_name/*.mp3obtiene una lista de nombres de archivos MP3, pero, en un directorio sin ninguno, permanece, literalmente, como directory_name/*.mp3. Esto se puede solucionar diciéndole al shell que los comodines que no coinciden con ningún archivo deberían simplemente desaparecer de la línea de comando:

encontrar . -tipo d -exec
        sh-c 'shopt -s nullglob;establecer -- "$0"/*.mp3; [ $# -eq 1 ]' {} \; -imprimir

(escrito todo como una línea) produce

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

Pero tienes el shell contando los archivos y estás pidiendo findque se haga la impresión, y findsolo mira los directorios. ¿Por qué no dejar que el shell imprima el nombre del archivo que ve?

encontrar . -tipo d -exec
        sh -c 'comprar -s nullglob; establecer -- "$0"/*.mp3; [ $# -eq 1 ]&& eco "$1"' {} \;

(es decir, si hay un archivo, echoel nombre del primero ( $1)) le dará lo que desea.

Respuesta2

Para Windows que usa PowerShell, suponiendo que ya se encuentre en la carpeta 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}

Salidas:

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

Desglosando el comando canalizado:

Obtenga todos los archivos y carpetas debajo del directorio actual

Get-ChildItem -recurse 

Haga coincidir solo los archivos según su máscara

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

Realizar agrupaciones basadas en la propiedad del directorio

Group-Object -Property Directory 

Seleccione solo los directorios con 1 elemento

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

Extraiga el nombre completo del archivo que coincida con su máscara de archivo en cada directorio

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

Respuesta3

Estoy seguro de que alguien tiene una mejor manera de hacerlo, pero este comando bash funcionará en Linux.

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

Este script realiza una prueba en la carpeta de cada archivo .mp3 y se repite para cada coincidencia en el mismo archivo. Por ejemplo, comprueba Música/Artista/Álbum 3 veces. Dependiendo de cuántos archivos mp3 tengas, esto podría llevar mucho tiempo (necesitarías una biblioteca bastante grande).

Si es así, puedes hacerlo un poco más complicado:

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

El resultado es el mismo, pero sólo comprobará cada carpeta una vez, lo que podría ahorrar mucho tiempo. El comando awk elimina cualquier nombre de carpeta duplicado.

Otra nota es que los archivos distinguen entre mayúsculas y minúsculas en Linux, por lo que esto ignorará por completo cualquiera de los archivos .MP3o .Mp3. Si desea que coincida con ellos, cambie las 3 instancias de mp3a[mM][pP]3

Respuesta4

Aquí hay una respuesta:

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

información relacionada