Contar arquivos em um diretório por extensão

15

Para fins de teste, eu gostaria de contar quantos arquivos de imagens estão dentro de um diretório, separando cada tipo de arquivo de imagem por extensão de arquivo (jpg = "yes". Isso porque mais tarde será útil para outro script que executará uma ação em cada extensão de arquivo). Posso usar algo como o seguinte apenas para arquivos JPEG?

jpg=""
count=`ls -1 *.jpg 2>/dev/null | wc -l`
if [ $count != 0 ]
then
echo jpg files found: $count ; jpg="yes"
fi

Considerando as extensões de arquivo jpg, png, bmp, raw e outras, devo usar um whileciclo para fazer isso?

watchmansky
fonte

Respostas:

14

Sugiro uma abordagem diferente, evitando os possíveis problemas de divisão de palavras de ls

#!/bin/bash

shopt -s nullglob

for ext in jpg png gif; do 
  files=( *."$ext" )
  printf 'number of %s files: %d\n' "$ext" "${#files[@]}"

  # now we can loop over all the files having the current extension
  for f in "${files[@]}"; do
    # anything else you like with these files
    :
  done 

done

Você pode fazer um loop sobre a filesmatriz com quaisquer outros comandos que deseja executar nos arquivos de cada extensão específica.


De maneira mais portável - ou para shells que não fornecem matrizes explicitamente - você pode reutilizar a matriz de parâmetros posicionais do shell, ou seja,

set -- *."$ext"

e depois substituir ${#files[@]}e ${files[@]}com $#e"$@"

chave de aço
fonte
23

Minha abordagem seria:

  1. Listar todos os arquivos no diretório
  2. Extrair sua extensão
  3. Classifique o resultado
  4. Contar as ocorrências de cada extensão

Mais ou menos assim (a última awkchamada é apenas para formatação):

ls -q -U | awk -F . '{print $NF}' | sort | uniq -c | awk '{print $2,$1}'

(supondo ao GNU lsaqui a -Uopção de pular a classificação como uma otimização. Ele pode ser removido com segurança sem afetar a funcionalidade, se não for suportado).

groxxda
fonte
mhmh ... depois devo filtrar cada extensão encontrada para fazer uma ação por ela?
Watchmansky
Depende do que você deseja fazer no final. Você pode dar mais informações?
groxxda
Meu objetivo: um script que processe cada arquivo de extensão (apenas arquivo de imagem) alterando o tamanho dos dados de entrada do usuário. Então, eu começo a partir de quantos arquivos jpg lá está, ao lado png, etc.
watchmansky
A solução dos aparafusadores de aço pode ser mais apropriada.
groxxda
2
Eu tinha tanto JPGe jpgarquivos, e queria-o de forma recursiva por isso a minha solução era escreverfind . -type f | awk -F . '{print tolower($NF)}' | sort | uniq -c | awk '{print $2,":",$1}'
Kristian
11

Isso percorre recursivamente arquivos e conta extensões que correspondem:

$ find . -type f | sed -e 's/.*\.//' | sort | uniq -c | sort -n | grep -Ei '(tiff|bmp|jpeg|jpg|png|gif)$'
   6 tiff
   7 bmp
  26 jpeg
  38 gif
  51 jpg
  54 png
Kit
fonte
6
find -type f | sed -e 's/.*\.//' | sort | uniq -c
Neik
fonte
3
Não se esqueça de um diretório inicial com find. Além disso, pode ajudar futuros leitores dessas respostas se você fornecer uma breve explicação sobre sua solução (caso eles desejem modificá-la para um caso um pouco diferente).
Jeff Schaller
Quão bem esta solução lida com nomes de caminhos que contêm espaços? Novas linhas?
dhag
1
findpadrão para o diretório atual, que é como eu uso isso. Eu não acho que Deus pretendia que os nomes de arquivos tivessem espaços neles, mas isso funciona bem para esse caso. Se você tem novas linhas, merece tudo o que recebe. Pensei em uma explicação, mas decidi que daria uma resposta muito longa, acho que a simplicidade é o que importa. 99% dos casos em 1% das vezes. Provavelmente é compatível com a versão 7.
Neik 22/10/2015
3

Talvez possa ficar mais curto

exts=( *.jpg *.png *.gif ); printf "There are ${#exts[@]}" extensions;
Valentin Bajrami
fonte
3

Qualquer coisa envolvida lsprovavelmente produzirá resultados inesperados com caracteres especiais (espaço e outros símbolos). Qualquer basismo (como matrizes) não é portátil. Qualquer coisa envolvida while readé geralmente lenta.

Por outro lado, findé MUITO flexível (muitas opções para filtrar), possui [pelo menos] duas sintaxes que são à prova de falhas para caracteres especiais ... e Escala bem no diretório grande.

Neste exemplo, usei o -inamenome da extensão em maiúsculas e minúsculas. Também restringi a -maxdepth 1respeitar a sua pergunta "no diretório atual". Em vez de contar o número de linhas, onde os nomes de arquivos podem incluir CR / LF, -print0imprimirá um byte NULL no final de cada nome de arquivo ... assim | tr -d -c "\000" | wc -lcomo a contagem precisa de arquivos (NULL bytes!).

extensions="jpg png gif"
for ext in $extensions; do
  c=$(find . -maxdepth 1 -iname "*.$ext" -print0 | tr -d -c "\000" | wc -c)
  if [ $c -gt 0 ]; then
    echo "Found $c  *.$ext files"

    find . -maxdepth 1 -iname "*.$ext" -print0 | xargs -0 -r -n1 DOSOMETHINGHERE
    # or #  find . -maxdepth 1 -iname "*.$ext" -exec "ls" "-l" "{}" ";"
  fi
done

PS -print0 | tr -d -c "\000" | wc -cpode ser substituído por -printf "\000" | wc -cou mesmo -printf '\n' | wc -l.

Franklin Piat
fonte
0

pode apenas usar ls para algo tão simples como IMO

ls -l /opt/ssl/certs/*.pem | wc -l

ou

count=$(ls -l /some/folder/*.jpg | wc -l)

ou

ls *.{mp3,exe,mp4} 2>/dev/null | wc -l
Mike Q
fonte
-2

Se você tem certeza da extensão, você pode ir com findcomo

find *.jpeg | wc -l
Nithish JV
fonte
até que alguém crie touch $'foo\nbar.jpege seja contado duas vezes em vez de uma vez. Ou pior, alguém sabemkdir directory.jpeg; touch directory.jpeg/{1..100}.txt
Jeff Schaller