Lista recursivamente todos os diretórios que contenham um ou mais arquivos de imagem jpg

2

Estou tentando arrumar minhas fotos que, por várias razões históricas, estão espalhadas pelo meu sistema. Para permitir que eu comece nesta tarefa, tenho tentado usar a linha de comando para construir uma lista de todos os diretórios que contenham um ou mais arquivos jpg. Tenho certeza de que não preciso me preocupar em procurar outros formatos de arquivo de imagem, mas preciso permitir que o jpg apareça em maiúsculas e minúsculas.

Gostaria que cada nome de diretório aparecesse apenas uma vez na lista final. Para fornecer um exemplo, se eu tiver os seguintes diretórios, cada um deles contém um ou mais arquivos JPG ou JPG ....

~Mike/Pictures
~Mike/Pictures/London/Olympics
~Mike/Pictures/London
~Mike/Pictures/London/Holiday
~Mike/Photos
~Mike/Family History/Swaine

Eu gostaria que os resultados aparecessem com cada diretório listado apenas uma vez - independentemente do número de arquivos de imagem que ele possa conter - preferencialmente classificados e depois gravados em um arquivo

~Mike/Family History/Swaine
~Mike/Photos
~Mike/Pictures
~Mike/Pictures/London
~Mike/Pictures/London/Holiday
~Mike/Pictures/London/Olympics

Minhas habilidades de linha de comando simplesmente não estão à altura disso! Eu posso usar muitas das formas mais simples de comandos únicos, mas uma vez que eles se tornam complexos e / ou precisam ser canalizados, as coisas tendem a dar errado.

    
por Midahed 10.09.2017 / 19:53

3 respostas

2

Supondo que os arquivos de imagem JPEG tenham o sufixo .jpg :

find "$HOME" -type f -name '*.jpg' \
    -exec sh -c 'for d; do dirname "$d"; done' sh {} + | sort -u -o jpeg_dirs.txt

Isso depende de você não ter nomes de diretórios engraçados com novas linhas em seus nomes.

Com o GNU find :

find "$HOME" -type f -name '*.jpg' -printf '%h\n' | sort -u -o jpeg_dirs.txt

Estes comandos find encontrarão todas as imagens JPEG em seu diretório pessoal e imprimirão os nomes dos diretórios onde eles foram encontrados. O sort -u terá essa lista de nomes de diretório, classificará e removerá duplicados. O resultado será gravado no arquivo jpeg_dirs.txt no diretório atual.

    
por 10.09.2017 / 20:19
3

Uma maneira simples é listar todos os arquivos .jpg , depois remover os nomes base dos arquivos (a parte após a barra final) e remover duplicatas. Você pode usar sed para remover a parte de cada linha após a barra final. Existe um comando para remover duplicatas, que é chamado de uniq , mas assume entrada classificada; se você precisar classificar de qualquer maneira, pode deixar sort fazer a uniquificação.

find ~Mike -iname '*.jpg' | sed 's!/[^/]*$!!' | sort -u >directories_with_jpeg_files.txt

Isso pressupõe que nenhum dos diretórios ou arquivos envolvidos tenham uma nova linha em seu nome. Nomes de arquivos com novas linhas não aparecem em circunstâncias normais, mas tenha cuidado se os nomes de arquivos podem ter sido escolhidos por uma pessoa hostil (por exemplo, se você está processando arquivos que foram enviados para um servidor eo remetente pode escolher o nome do arquivo) .

Se houver diretórios contendo muitos arquivos JPEG e não muitos diretórios que contenham nenhum arquivo JPEG, esse método gasta muito tempo relatando arquivos redundantes. Não é possível dizer ao find para acertar um diretório quando ele encontrar algo nele. Mas você pode restringir a localização para diretórios e dizer para procurar um arquivo JPEG em cada diretório. Isso aumenta o custo para diretórios que não contêm arquivos JPEG, no entanto, pode ter um desempenho ruim se houver muitos diretórios JPEGless.

find ~Mike -type d -exec sh -c '
    for d do
      set -- "$d/*.[Jj][Pp][Gg]";
      if [ -e "$1" ]; then printf %s\n "$d"; fi
    done
' sh {} + | sort -u >directories_with_jpeg_files.txt

Como alternativa, em zsh, é possível usar o curinga ** para percorrer diretórios recursivamente, (#i) para corresponder ao seguinte componente do caminho sem distinção entre maiúsculas e minúsculas para tornar o padrão **/(#i)*.jpg matching *.jpg e *.JPG (e .Jpg e assim por diante) em uma árvore de diretórios inteira. Adicione o modificador de histórico h em um qualificador de glob para extrair a parte do diretório. Coloque isso em uma variável de matriz dirs=(…) e extraia os elementos exclusivos dessa matriz com o sinalizador de expansão do parâmetro u .

set -o extendedglob # for (#i); best in ~/.zshrc
dirs=(~Mike/**/(#i)*.jpg(:h))
print -lr -- ${(u)dirs} >directories_with_jpeg_files.txt

O equivalente do método de verificação por diretório acima é usar o qualificador e glob.

print -lr ~Mike/**/*(/e\''set -- $REPLY/*.(#i)jpg(N[1]); (($# != 0))'\') >directories_with_jpeg_files.txt
    
por 10.09.2017 / 22:55
0
find . -iname '*.jpg' -execdir sh -c 'pwd' _ {} + | sort -u > dirs_with_jpegs.txt

Deve funcionar bem o suficiente, supondo que sua implementação de find suporte -execdir (provavelmente é). -execdir executa um comando no diretório onde está o arquivo encontrado. Neste caso, executamos o comando pwd , que imprime o nome do diretório. Envolvemos o comando com sh -c para remover argumentos. (Algumas (todas?) Implementações de find requerem a substituição do argumento {} , que seria a lista de arquivos jpeg no diretório atual. Queremos ignorar essa lista e apenas imprimir o diretório.)

    
por 11.09.2017 / 01:41