
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 *.mp3
archivos) es que está diciendo -le 1
en lugar de -eq 1
. Parece que informará directorios con un archivo MP3 o menos. Pero cambiar -le 1
a -eq 1
no funciona. Resulta que el problema es que, en un directorio con ≥ un archivo MP3,
directory_name/*.mp3
obtiene 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 find
que se haga la impresión, y find
solo 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, echo
el 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 .MP3
o .Mp3
. Si desea que coincida con ellos, cambie las 3 instancias de mp3
a[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