Como posso encontrar pastas contendo arquivos x de um tipo específico e gerar esses caminhos no OSX?

2

Eu tenho este script para o OSX para encontrar pastas que contenham apenas um arquivo, e se esse arquivo for um arquivo de áudio exibindo o caminho do arquivo de áudio

find "$1" -type d -exec sh -c '[[ $(find "$0" -mindepth 1 | wc -l) -eq 1 ]] 
&& [[ $(find "$0" -mindepth 1 -type d | wc -l) -eq 0 ]]  
&& find "$0"' {} \; |egrep ".mp4|.mp3|.ogg|.flac|.wma|.m4a"

ou seja, use como

./findodd.sh /Users/paul/Music

mas há duas melhorias que preciso:

  1. O que posso alterar para listar arquivos em pastas contendo 2 arquivos, 3 arquivos ectera, seria ainda melhor se isso pudesse ser passado como um parâmetro

  2. Atualmente ele encontra pastas contendo apenas um arquivo, e esse arquivo deve ser um arquivo de áudio, mas o que eu realmente quero é encontrar pasta contendo apenas um arquivo de áudio, ou seja, se a pasta contiver três arquivos, mas apenas um é um arquivo de áudio que eu quero que o arquivo de áudio seja listado.

obrigado Paul

    
por Paul Taylor 05.03.2014 / 11:53

3 respostas

2
$ find
.
./folder3
./folder3/quux.txt
./folder1
./folder1/test.mp3
./folder1/test.txt
./folder1/test.wma
./folder2
./folder2/bar.txt
./folder2/foo.txt
./folder2/test.ogg

Exemplo é executado:

$ ./findaudio.sh /tmp/findaudio 1
/tmp/findaudio/folder2/test.ogg

$ ./findaudio.sh /tmp/findaudio 2
/tmp/findaudio/folder1/test.mp3
/tmp/findaudio/folder1/test.wma

# The first parameter defaults to the current directory and
# the second parameter defaults to 1 so this works as well:
$ ./findaudio.sh
./folder2/test.ogg

E aqui o código:

#!/bin/bash

shopt -s nullglob

find "${1:-.}" -type d | while read dir; do
        files=( "${dir}"/*.{mp4,mp3,ogg,flac,wma,m4a} )
        IFS=$'\n'
        (( ${#files[@]} == ${2:-1} )) && echo "${files[*]}"
done

Ele itera sobre todos os subdiretórios do diretório especificado e usa globbing para ler todos os nomes de arquivos de áudio do subdiretório atual no array files . Se o tamanho da matriz corresponder ao valor desejado, basta imprimir os nomes dos arquivos separados por uma nova linha.

EDIT: Esta é a minha abordagem anterior baseada no pressuposto de que você queria imprimir as pastas, não os nomes de arquivos em questão. Vou deixar aqui para referência futura.

$ find . \( -name '*.ogg' -o -name '*.wma' -o -name '*.mp3' \) -printf "%h\n" | uniq -u
./folder2

O que isto faz é encontrar todos os arquivos com as extensões de áudio listadas e imprimir apenas seus componentes de diretório em vez do caminho completo. Isso fornece uma lista de pastas pai para todos os arquivos de áudio. O uniq ignora as linhas não exclusivas que devem fornecer o resultado desejado, ou seja, apenas as pastas de impressão que contêm exatamente um arquivo de áudio.

Em teoria, isso também deve ser um pouco mais rápido do que sua tentativa anterior.

Você pode melhorar isso para satisfazer seu primeiro ponto, contando as linhas duplicadas e imprimindo apenas as pastas que correspondem à sua contagem solicitada. Uma solução ingênua seria:

$ find . \( -name '*.ogg' -o -name '*.wma' -o -name '*.mp3' \) -printf "%h\n" | uniq -c | awk -v count=1 '$1==count'
1 ./folder2

$ find . \( -name '*.ogg' -o -name '*.wma' -o -name '*.mp3' \) -printf "%h\n" | uniq -c | awk -v count=2 '$1==count'
2 ./folder1

embora seja melhor fundir o uniq -part e o lado direito do canal em uma única linha awk .

    
por 05.03.2014 / 13:53
1

SEGUNDA TENTATIVA

OK, depois de tentar isso sozinho na minha pasta Música, esta é a solução para suas solicitações:

COMMAND='[[ $(find "$0" -maxdepth 2 |egrep "\.mp4|\.mp3|\.ogg|\.flac|\.wma|\.m4a"| wc -l) == '$2' ]] && echo "$0"'
find $1 -type d -exec sh -c "$COMMAND" {} \;

Então, havia algumas coisas erradas com o seu script:

  1. Você estava usando mindepth em vez de maxdepth .
  2. Os pontos (.) do seu egrep teriam correspondido a qualquer caractere. Então, .wma teria correspondido a 'Snowman.txt'.
  3. Você não precisou fazer o segundo teste para o tipo 'd', pois apenas os diretórios são passados para o comando shell.

Notas sobre o meu script:

  1. o uso é: findodd.sh <top_folder> <no_of_files>
  2. As cotações são críticas. A definição de COMMAND é, na verdade, dois literais de string em ambos os lados do $2 . Isso é realmente importante.
  3. Apenas lista as pastas que contêm os arquivos, não os arquivos em si. Para fazer este último, você teria que substituir o echo "$0" por outro find .

Agora estou testando em uma máquina Arch Linux, e meu shell é 'bash', então não tenho idéia se isso funcionará no OSX, já que todos os shells NÃO são criados da mesma forma. : -)

PRIMEIRA TENTATIVA ANTERIOR:

Hmmmm. Eu não sei o quão similar o OSX é ao Unix / Linux, mas eu vou dar uma facada.

A resposta para ambas as suas perguntas, acredito, está no primeiro teste do comando 'sh -c'. Essa é a parte que diz:

$(find "$0" -mindepth 1 | wc -l) -eq 1

Para passar um segundo parâmetro para o seu script para o número de arquivos, você deve poder apenas alterar o '1' para $ 2, então o teste seria:

$(find "$0" -mindepth 1 | wc -l) -eq $2

Não coloque aspas ao redor do $2 , porque senão ele será interpretado como o segundo parâmetro passado para o comando 'sh -c', não para o seu script.

A linha de comando seria então:

./findodd.sh /Users/paul/Music 2

Para atingir seu segundo requisito, pelo que entendi, você precisa colocar o comando egrep no primeiro teste, assim:

$(find "$0" -mindepth 1 |egrep ".mp4|.mp3|.ogg|.flac|.wma|.m4a"| wc -l) -eq $2

Você pode ter que assistir as citações, no entanto.

De qualquer forma, dê uma chance e nos avise.

    
por 10.03.2014 / 22:57
0

Você poderia implementar isso no Python fazendo algo assim:

#!/usr/bin/env python

import fnmatch
import os
import sys

if len(sys.argv) != 3 or \
        not sys.argv[1].isdigit() or \
        not os.path.exists(sys.argv[2]):
    print "Usage: %s [number of files] [search root]" % sys.argv[0]
    sys.exit(1)

num_files = int(sys.argv[1])
search_root = sys.argv[2]

# this must be a tuple to work with endswith()
audio_extensions = (
    'mp4',
    'mp3',
    'ogg',
    'flac',
    'wma',
    'm4a',
)

for dirpath, dirnames, filenames in os.walk(search_root):
    audio_files = [f for f in filenames if f.endswith(audio_extensions)]
    if len(audio_files) == num_files:
        print "\n".join([os.path.join(dirpath, f) for f in audio_files])

Se você chmod +x findodd.py , poderá executá-lo da mesma forma que o seu script atual, por exemplo:

./findodd.py 1 /Users/paul/Music
    
por 11.03.2014 / 01:00