Como contar o número de arquivos em cada diretório?


104

Eu sou capaz de listar todos os diretórios por

find ./ -type d

Tentei listar o conteúdo de cada diretório e contar o número de arquivos em cada diretório usando o seguinte comando

find ./ -type d | xargs ls -l | wc -l

Mas isso somava o número total de linhas retornadas por

find ./ -type d | xargs ls -l

Existe uma maneira de contar o número de arquivos em cada diretório?


Você está procurando uma maneira de contar o número de arquivos em cada um dos subdiretórios diretamente sob ./?
Tuxdude

5
Como isso é uma questão fora do tópico ?? Eu gostaria de ver os comentários dos eleitores próximos com razão! Se estiver fora do tópico, a que lugar pertence? superusuário? Acho que não ..
InfantPro'Aravind '

6
shell-script, batch-script estão sob o escopo de programação!
InfantPro'Aravind '

Eu estava prestes a postar a solução Pythonic, então percebi que a questão está encerrada.
anatoly techtonik

votou para reabri-lo. Pode haver outras respostas que podem ser úteis em muitas situações (incluindo programação de script, que é o motivo pelo qual cheguei a esta pergunta).
lepe

Respostas:


110

Supondo que você tenha o GNU find, deixe-o encontrar os diretórios e deixe o bash fazer o resto:

find . -type d -print0 | while read -d '' -r dir; do
    files=("$dir"/*)
    printf "%5d files in directory %s\n" "${#files[@]}" "$dir"
done

2
É apenas uma versão ligeiramente diferente da anterior, então: (dica: é classificado por nome e está em csv) para x em find . -maxdepth 1 -type d | sort; do y = find $x | wc -l; echo $ x, $ y; feito
pcarvalho

5
Muito bom! Colocando em uma única linha (portanto, é confortável para uso direto no shell):find . -type d -print0 | while read -d '' -r dir; do files=("$dir"/*); printf "%5d files in directory %s\n" "${#files[@]}" "$dir"; done
lucaferrario

13
Eu precisava obter o número de todos os arquivos (contagem recursiva) em cada subdiretório. Essa modificação oferece a você: find . -maxdepth 1 -type d -print0 | while read -d '' -r dir; do num=$(find $dir -ls | wc -l); printf "%5d files in directory %s\n" "$num" "$dir"; done
OmidS

1
@Kory O seguinte fará isso:find . -maxdepth 1 -type d -print0 | while read -d '' -r dir; do num=$(find "$dir" -ls | wc -l); printf "%5d files in directory %s\n" "$num" "$dir"; done | sort -rn -k1
OmidS

1
@OmidS Excelente oneliner, mas $dirdeve estar entre aspas em seu primeiro comentário para lidar corretamente com nomes de diretórios com espaços em branco. :find . -maxdepth 1 -type d -print0 | while read -d '' -r dir; do num=$(find "$dir" -ls | wc -l); printf "%5d files in directory %s\n" "$num" "$dir"; done
Radek Daniluk

183

Isso imprime a contagem de arquivos por diretório para o nível de diretório atual:

du -a | cut -d/ -f2 | sort | uniq -c | sort -nr

9
De longe, a melhor (e mais elegante) solução se alguém deseja listar o número de arquivos em diretórios de nível superior recursivamente.
itoctopus

13
Isso tem dois problemas: conta um arquivo por diretório a mais do que realmente existe e fornece uma linha inútil contendo o tamanho do diretório atual como "1 tamanho ". Ambos podem ser corrigidos com du -a | sed '/.*\.\/.*\/.*/!d' | cut -d/ -f2 | sort | uniq -c. Adicione | sort -nrpara classificar pela contagem em vez do nome do diretório.
sobremesa de

3
Gostaria de salientar que isso funciona no OSX também. (Copiar e colar conselhos do Linux em um shell OSX geralmente não funciona.)
Pistos

2
ele busca o tamanho desnecessário por du -a. A melhor maneira é usar o comando find. mas a ideia principal é exatamente a mesma :)
Znik

5
encontrar . -tipo f | cut -d / -f2 | classificar | uniq -c | sort -nr # corrige problemas mencionados pela sobremesa
jcomeau_ictx

28
find . -type f | cut -d/ -f2 | sort | uniq -c
  • find. -type f para encontrar todos os itens do tipo de arquivo
  • cut -d/ -f2 para cortar sua pasta específica
  • sort para classificar a lista de nomes de pastas
  • uniq -c para retornar o número de vezes que cada nome de pasta foi contado

8
Isso é muito melhor do que a resposta aceita, pois você obtém um resumo dos diretórios de nível superior!
Jason Floyd

3
Esta deve ser a resposta aceita. Simples e compreensível.
xssChauhan

1
A melhor resposta que deve ser aceita é esta.
Loretoparisi

1
Simples, elegante e perfeito para minhas necessidades.
RichR

Perfeito. E pode ser estendido para contar subdiretórios, substituindo os especificadores de campo por uma lista de especificadores de campo. Por exemplo ,:find . -type f | cut -d/ -f2,3 | sort | uniq -c
algal

15

Você pode organizar para localizar todos os arquivos, remover os nomes dos arquivos, deixando uma linha contendo apenas o nome do diretório para cada arquivo e, em seguida, contar o número de vezes que cada diretório aparece:

find . -type f |
sed 's%/[^/]*$%%' |
sort |
uniq -c

O único problema nisso é se você tem nomes de arquivos ou diretórios contendo um caractere de nova linha, o que é bastante improvável. Se você realmente precisa se preocupar com novas linhas em nomes de arquivos ou diretórios, sugiro que os encontre e corrija para que não contenham novas linhas (e silenciosamente persuadir a parte culpada do erro de seus caminhos).


Se você estiver interessado na contagem dos arquivos em cada subdiretório do diretório atual, contando todos os arquivos em quaisquer subdiretórios junto com os arquivos no subdiretório imediato, então eu adaptaria o sedcomando para imprimir apenas o diretório de nível superior:

find . -type f |
sed -e 's%^\(\./[^/]*/\).*$%\1%' -e 's%^\.\/[^/]*$%./%' |
sort |
uniq -c

O primeiro padrão captura o início do nome, o ponto, a barra, o nome até a próxima barra e a barra, e substitui a linha apenas pela primeira parte, então:

./dir1/dir2/file1

é substituído por

./dir1/

A segunda substituição captura os arquivos diretamente no diretório atual; eles não têm uma barra no final e são substituídos por ./. A classificação e a contagem funcionam apenas com o número de nomes.


1
Isso não produz nomes de diretório que não contenham nenhum arquivo. Não tenho certeza se isso é necessário.
Austin Phillips

É verdade que não. Não é particularmente trivial consertar isso, uma vez que os nomes de diretórios vazios não são garantidos nem mesmo para aparecer na saída de find. Alguns podem: se houver um arquivo dir1/dir2/dir3/file1, mas dir1/dir2contiver apenas subdiretórios (sem arquivos simples), você pode inferir sua presença. Mas se dir1/dir4não houver arquivos, seu nome simplesmente não aparecerá.
Jonathan Leffler

Resposta muito útil se você deseja apenas ver os subdiretórios do diretório atual.
xixixao

Só passei para dizer obrigado. 3 anos depois que isso foi postado, eu estava tentando contar as pastas de segundo nível por pasta. Sua postagem me salvou potencialmente muitas horas de mexer com sed, find e quem sabe o que mais
Corvin

13

Esta é uma maneira de fazer isso, mas provavelmente não a mais eficiente.

find -type d -print0 | xargs -0 -n1 bash -c 'echo -n "$1:"; ls -1 "$1" | wc -l' --

Fornece uma saída como esta, com o nome do diretório seguido pela contagem de entradas nesse diretório. Observe que a contagem de saída também incluirá entradas de diretório que podem não ser o que você deseja.

./c/fa/l:0
./a:4
./a/c:0
./a/a:1
./a/a/b:0

Parece muito caro para executar 3 comandos ( bash, ls, wc) para cada diretório encontrado find.
Jonathan Leffler

@JonathanLeffler Concordo, daí a primeira linha da minha resposta. Sua solução é melhor.
Austin Phillips

legal é isso que estou procurando, posso perguntar o que é o '-' no final?
uma vez em

1
@once O - pertence ao comando bash que será gerado por xargs. A partir man bash, A -- signals the end of options and disables further option processing. Nesse caso, evitaria que um arquivo com nome incorreto encontrado como parte da localização se tornasse parte do processamento de argumento para bash.
Austin Phillips,

8

A solução de qualquer outra pessoa tem uma desvantagem ou outra.

find -type d -readable -exec sh -c 'printf "%s " "$1"; ls -1UA "$1" | wc -l' sh {} ';'

Explicação:

  • -type d: estamos interessados ​​em diretórios.
  • -readable: Só os queremos se for possível listar os arquivos neles. Observe que findainda emitirá um erro quando tentar pesquisar por mais diretórios neles, mas isso evita chamá -exec-los.
  • -exec sh -c BLAH sh {} ';': para cada diretório, execute este fragmento de script, com $0definido como she $1definido como o nome do arquivo.
  • printf "%s " "$1": imprime portável e minimamente o nome do diretório, seguido por apenas um espaço, não uma nova linha.
  • ls -1UA: lista os arquivos, um por linha, na ordem do diretório (para evitar travar o tubo), excluindo apenas os diretórios especiais .e..
  • wc -l: conte as linhas

1
Modificação para mostrar o arquivo conta primeiro na linha e para classificar por eles:find -type d -readable -exec sh -c 'ls -1UA "$1" | wc -l | tr -d "\n" ; printf "\t%s\n" "$1" ' sh {} ';' | sort -n
Evgeni Sergeev

ele executa o shell muitas vezes, então é lento e utiliza recursos intensamente.
Znik de

6

Versão ligeiramente modificada da resposta de Sebastian usando em findvez de du(para excluir a sobrecarga relacionada ao tamanho do arquivo que duprecisa ser executada e nunca é usada):

 find ./ -mindepth 2 -type f | cut -d/ -f2 | sort | uniq -c | sort -nr

-mindepth 2parâmetro é usado para excluir arquivos no diretório atual. Se você removê-lo, verá várias linhas como as seguintes:

  234 dir1
  123 dir2
    1 file1
    1 file2
    1 file3
      ...
    1 fileN

(muito parecido ducom a variante com base em)

Se você também precisar contar os arquivos no diretório atual, use esta versão aprimorada:

{ find ./ -mindepth 2 -type f | cut -d/ -f2 | sort && find ./ -maxdepth 1 -type f | cut -d/ -f1; } | uniq -c | sort -nr

O resultado será como o seguinte:

  234 dir1
  123 dir2
   42 .

5

Isso também pode ser feito com um loop sobre ls em vez de encontrar

for f in */; do echo "$f -> $(ls $f | wc -l)"; done

Explicação:

for f in */; - percorre todos os diretórios

do echo "$f -> - imprimir cada nome de diretório

$(ls $f | wc -l) - ligue para este diretório e conte as linhas


1
Isso não funcionará corretamente se os nomes dos diretórios contiverem espaços em branco.
Xylol

Experimentefor f ./* ; do echo $f $(ls "$f" | wc -l); done
4ndt3s

3

Isso deve retornar o nome do diretório seguido pelo número de arquivos no diretório.

findfiles() {
    echo "$1" $(find "$1" -maxdepth 1 -type f | wc -l)
}

export -f findfiles

find ./ -type d -exec bash -c 'findfiles "$0"' {} \;

Exemplo de saída:

./ 6
./foo 1
./foo/bar 2
./foo/bar/bazzz 0
./foo/bar/baz 4
./src 4

O export -fé necessário porque o -execargumento de findnão permite a execução de uma função bash a menos que você invoque o bash explicitamente e precise exportar a função definida no escopo atual para o novo shell explicitamente.


Isso parece excessivamente complicado. Também me parece que dá contagens cumulativas para uma hierarquia de diretório como ./dir1/dir2/dir3(contando os arquivos em dir1e seus subdiretórios todos juntos, em vez de contar os arquivos em dir1/dir2/dir3separadamente daqueles em dir1/dir2e ambos separadamente daqueles em /dir1).
Jonathan Leffler

Eu entendi que era o que o autor queria. Se não for esse o caso, concordo que a resposta não é relevante para a pergunta.
Tuxdude

1
@JonathanLeffler - Ok, lendo a pergunta mais uma vez, percebi que você está certo - modifiquei a resposta de acordo.
Tuxdude

2

Eu combinei resposta de Jackman @glenn e resposta da @ pcarvalho (na lista de comentário, há algo errado com a resposta de pcarvalho porque a função de controle extra de estilo de caracter ' ` '(crase)).

Meu script pode aceitar path como um augument e ordenar a lista de diretórios como ls -l, também pode lidar com o problema de "espaço no nome do arquivo" .

#!/bin/bash
OLD_IFS="$IFS"
IFS=$'\n'
for dir in $(find $1 -maxdepth 1 -type d | sort); 
do
    files=("$dir"/*)
    printf "%5d,%s\n" "${#files[@]}" "$dir"
done
FS="$OLD_IFS"

Minha primeira resposta em stackoverflow, e espero que possa ajudar alguém ^ _ ^


1

encontrar . -tipo f -printf '% h \ n' | classificar | uniq -c

dá por exemplo:

  5 .
  4 ./aln
  5 ./aln/iq
  4 ./bs
  4 ./ft
  6 ./hot

0

Tentei com alguns dos outros aqui, mas acabei com subpastas incluídas na contagem de arquivos quando eu queria apenas os arquivos. Isso é impresso ./folder/path<tab>nnncom o número de arquivos, sem incluir subpastas, para cada subpasta na pasta atual.

for d in `find . -type d -print` 
do 
  echo -e "$d\t$(find $d -maxdepth 1 -type f -print | wc -l)"
done

0

Uma maneira fácil de localizar arquivos recursivamente de um determinado tipo. Nesse caso, arquivos .jpg para todas as pastas no diretório atual:

find . -name *.jpg -print | wc -l


0

Um comando milagroso super rápido, que percorre arquivos recursivamente para contar o número de imagens em um diretório e organizar a saída por extensão de imagem:

find . -type f | sed -e 's/.*\.//' | sort | uniq -c | sort -n | grep -Ei '(tiff|bmp|jpeg|jpg|png|gif)$'

Créditos: https://unix.stackexchange.com/a/386135/354980


0

Essa pode ser outra maneira de navegar pelas estruturas de diretório e fornecer resultados detalhados.

find . -type d  | awk '{print "echo -n \""$0"  \";ls -l "$0" | grep -v total | wc -l" }' | sh 

0

Editei o script para excluir todos os node_modulesdiretórios dentro do analisado.

Isso pode ser usado para verificar se o número de arquivos do projeto está excedendo o número máximo que o inspetor de arquivos pode manipular.

find . -type d ! -path "*node_modules*" -print0 | while read -d '' -r dir; do
    files=("$dir"/*)
    printf "%5d files in directory %s\n" "${#files[@]}" "$dir"
done

Para verificar o máximo de arquivos que seu sistema pode assistir:

cat /proc/sys/fs/inotify/max_user_watches

node_modules A pasta deve ser adicionada aos caminhos excluídos do IDE / editor em sistemas lentos, e a contagem de outros arquivos idealmente não deve exceder o máximo (que pode ser alterado).


-1

Isso dará a contagem geral.

for file in */; do echo "$file -> $(ls $file | wc -l)"; done | cut -d ' ' -f 3| py --ji -l 'numpy.sum(l)'

Não, não vai. Ele considerará apenas um nível de subdiretórios.
Kusalananda
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.