
Eu tenho uma pasta Música e o conteúdo é assim:
Música -ArtistaA -Song1.mp3 -ÁlbumA -Song2.mp3 -Song2.m4a -ÁlbumB -Song1.mp3 -Song2.mp3 -Song3.mp3 -ÁlbumC -Song1.mp3 -Song1.jpg -AlgumaPasta1 -Song1.mp3 -ArtistaB -Song1.mp3 -ArtistaC -Song1.mp3 -Song2.mp3 -Song3.mp3 -AlgumaPasta2 -Song1.jpg -Song2.jpg -AlgumaPasta3
Estou procurando uma saída como esta:
ArtistA/Song1.mp3 ArtistA/ÁlbumA/Song2.mp3 ArtistA/AlbumC/Song1.mp3 ArtistA/AlbumC/SomeFolder1/Song1.mp3 ArtistB/Song1.mp3
Então, não quero diretórios com apenas um arquivo, quero diretórios com apenas um mp3 - independente de subdiretórios ou outros arquivos. Também não quero diretórios vazios ou diretórios que contenham mais de um mp3.
Não me oponho a um script de shell para Linux. No entanto, não quero compilar um programa em nenhum dos sistemas operacionais.
Eu tentei:
find . -type d -exec sh -c 'set -- "$0"/*.mp3; [ $# -le 1 ]' {} \; -print
mas isso me dá esta saída:
. ./ArtistaB ./ArtistaA ./ArtistA/ÁlbumA ./ArtistaA/ÁlbumC ./ArtistA/AlbumC/SomeFolder1 ./ArtistC/SomeFolder2 ./ArtistC/SomeFolder3
que está perto, mas não é exatamente o que eu quero. Existem duas pastas vazias e nenhum nome de arquivo.
Responder1
À primeira vista, a razão pela qual seu primeiro comando está relatando as pastas vazias (aquelas sem *.mp3
arquivos) é que você está dizendo -le 1
em vez de -eq 1
. Parece que reportará diretórios com um arquivo MP3 ou menos. Mas mudar -le 1
para -eq 1
não funciona. Acontece que o problema é que, em um diretório com ≥ um arquivo MP3,
directory_name/*.mp3
você obtém uma lista dos nomes dos arquivos MP3, mas, em um diretório sem nenhum, permanece, literalmente, como directory_name/*.mp3
. Isso pode ser corrigido informando ao shell que os curingas que não correspondem a nenhum arquivo devem simplesmente desaparecer da linha de comando:
encontrar . -tipo d -exec sh-c'shopt -s nullglob;definir -- "$0"/*.mp3; [ $# -eq 1 ]' {} \; -imprimir
(digitado tudo como uma linha) produz
./ArtistA
./ArtistA/AlbumA
./ArtistA/AlbumC
./ArtistA/AlbumC/SomeFolder1
./ArtistB
Mas você tem o shell contando os arquivos e está pedindo find
para fazer a impressão – e find
está olhando apenas os diretórios. Por que não deixar o shell imprimir o nome do arquivo que vê?
encontrar . -tipo d -exec sh -c 'shopt -s nullglob; definir -- "$0"/*.mp3; [ $# -eq 1 ]&& eco "$1"'{}\;
(ou seja, se houver um arquivo, echo
o nome do primeiro ( $1
)) fornecerá o que você deseja.
Responder2
Para Windows usando PowerShell, supondo que você já esteja na pasta 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}
Saídas:
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
Dividindo o comando canalizado:
Obtenha todos os arquivos e pastas do diretório atual
Get-ChildItem -recurse
Combine apenas os arquivos com base na sua máscara
Where-Object {$_.name -match ".mp3"})
Execute o agrupamento com base na propriedade do diretório
Group-Object -Property Directory
Selecione apenas os diretórios com 1 item
Where-Object {$_.Count -eq 1}
Extraia o nome completo do arquivo que corresponde à sua máscara de arquivo em cada diretório
ForEach-Object { (Get-ChildItem $_.Name -Filter "*.mp3").FullName}
Responder3
Tenho certeza de que alguém tem uma maneira melhor de fazer isso, mas esse comando bash funcionará no 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 um teste na pasta de cada arquivo .mp3, repetindo-se para cada correspondência no mesmo arquivo. Por exemplo, ele verifica Music/ArtistA/AlbumB 3 vezes. Dependendo de quantos arquivos mp3 você possui, isso pode levar muito tempo (você precisaria de uma biblioteca bem grande).
Nesse caso, você pode complicar um pouco mais:
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
O resultado é o mesmo, mas só verificará cada pasta uma vez, o que pode economizar muito tempo. O comando awk remove quaisquer nomes de pasta duplicados.
Outra observação é que os arquivos diferenciam maiúsculas de minúsculas no Linux, portanto, isso ignorará totalmente qualquer arquivo .MP3
ou . .Mp3
Se você quiser que corresponda a eles, altere todas as 3 instâncias de mp3
para[mM][pP]3
Responder4
Aqui está uma resposta:
find Music -name "*.mp3" -exec dirname {} \; |
sort |
uniq -c |
while read count parent
do
if [[ $count -eq 1 ]]
then
echo "$parent"/*.mp3
fi
done