Obtém contagem de arquivos recursiva (como 'du', mas número de arquivos em vez de tamanho) [duplicado]

1

Como estou tendo problemas de desempenho com o rsnapshot, gostaria de identificar diretórios com grande número de arquivos de forma recursiva. Eu acho que o problema não é o tamanho dos arquivos, mas a contagem de arquivos em subdiretórios em particular, porque as gerações (daily.0, daily.1, ...) não são voláteis e só tem poucas mudanças em relação ao número total de arquivos. arquivos.

O comando Unix du seria exatamente o que eu quero, se retornasse apenas a contagem de arquivos e não a soma dos tamanhos dos arquivos.

Eu já tenho um script bash que gera a contagem de arquivos de todos os diretos subdiretórios (recursing em subdiretórios), mas é complicado de usar, porque eu tenho que cavar cada vez mais fundo e sempre ter que esperar.

Encontrou também um script pesquisando profundamente , mas não resumindo a contagem de arquivos dos subdiretórios. Ele mostra apenas o número de arquivos nesse diretório, não de seus filhos.

Não precisa ser um script de shell - estou aberto a outras linguagens de script como Ruby, Python, Perl, JavaScript, ...

Exemplo:

dir1/
   file1
   subdir1/
       file2, file3, file4, file5
   subdir2/
       file6, file7, file8
       subdir3/
           file9
dir2/
    fileA, fileB

Saída desejada (listando subdiretórios e resumindo até o topo):

4   dir1/subdir1
1   dir1/subdir2/subdir3
4   dir1/subdir2
9   dir1/
2   dir2/

O que eu não quero deseja (somente listando totais):

9   dir1/
2   dir2/

e não (listando apenas a contagem de arquivos do diretório . ):

4   dir1/subdir1
1   dir1/subdir2/subdir3
3   dir1/subdir2
1   dir1/
2   dir2/
    
por hgoebl 02.08.2015 / 16:05

3 respostas

2

Tente algo assim:

find . -type f | perl -aF/ -lne 'for (my $i=0; $i < @F-1; ++$i) { print join("/",@F[0...$i]); }' | sort | uniq -c

find . -type f imprime arquivos:

./dir1/subdir2/file8
./dir1/subdir2/file7
./dir1/subdir2/subdir3/file9
./dir1/subdir2/file6
./dir1/file1
...

perl -aF/ -lne 'for (my $i=0; $i < @F-1; ++$i) { print join("/",@F[0...$i]); }' converte cada nome de arquivo ./a/b/c em um conjunto de diretórios . , ./a , ./a/b

Nota:

não funciona com novas linhas em nomes de arquivos. Você pode usar -print0 em find , -0 em perl e colocar contadores para cada diretório em hash.

Editar:

Inspirado pela resposta do @Gilles :

find . -depth -print0 |
perl -0 -ne '
my $depth = tr!/!/!;
for (my $i = $prev_depth; $i <= $depth; ++$i) { $totals[$i] = 0; }
if ( -f $_ ) {
  for (my $i = 0; $i <= $depth; ++$i) { ++$totals[$i]; }
} else {
  print "$totals[$depth]\t$_\n";
}
$prev_depth = $depth;
'

Funciona bem com novas linhas em nomes de arquivos. Funciona bem com diretórios vazios. Não requer sort | uniq -c adicional.

    
por 02.08.2015 / 18:11
1

Se você tiver find (que pode ser usado para percorrer todos os arquivos em um diretório, incluindo todos os arquivos em subdiretórios do diretório) e wc (que conta o número de linhas em um arquivo), então que tal o one-liner

find <directory> | wc

onde <directory> é o diretório no qual você deseja contar todos os arquivos. Isso imprime três números; o primeiro é o número de linhas encontradas retornadas. Eu acho que find , por padrão, encontra arquivos e diretórios, então isso dará a contagem total do número de arquivos e diretórios em <directory> (incluindo <directory> em si).

find é um comando extremamente flexível. Se você está genuinamente interessado apenas em arquivos e não quer contar diretórios, então

find <directory> -type f | wc

fará o truque. Por exemplo, para contar todos os arquivos contidos (ainda que profundamente) no diretório atual, você pode fazer

find . -type f | wc

Advertências: Por padrão, find não seguirá links simbólicos etc; Se seus arquivos estão em vários sistemas de arquivos diferentes ou o que você tem, então você deve olhar para a página de manual para find , porque ele pode ser configurado para lidar com praticamente qualquer coisa. Note também que wc está contando linhas, então se você tiver arquivos com nomes que tenham novas linhas (tecnicamente possíveis, mas não tanto quanto eu saiba uma boa idéia em geral) ou algo assim, você obterá respostas engraçadas .

    
por 02.08.2015 / 16:49
1

Com base no meu comentário, uma variação pode fazer o que você deseja:

find . -depth -type d -exec /bin/sh -c 'printf "%5d %s\n" "$(find {} -type f -printf . | wc -c)" "{}"' \;

(o que a brigada propriamente dita certamente vai me acertar para calcular o resultado para subdiretórios mais profundos várias vezes e esperar que o cache do sistema de arquivos tenha os metadados inteiros da árvore em algum ponto, e também gerar uma nova shell toda vez, mas é um começo.)

Na sua estrutura de exemplo, recebo:

    4 ./dir1/subdir1
    1 ./dir1/subdir2/subdir3
    4 ./dir1/subdir2
    9 ./dir1
    2 ./dir2
   11 .

(para excluir o diretório de trabalho atual, altere o% externofind . para find * ou use find . -mindepth 1

    
por 02.08.2015 / 18:11