Contagem rápida de arquivos do Linux para um grande número de arquivos

137

Estou tentando descobrir a melhor maneira de encontrar o número de arquivos em um diretório específico quando há um número muito grande de arquivos (> 100.000).

Quando existem muitos arquivos, a execução ls | wc -lleva muito tempo para ser executada. Acredito que seja porque está retornando os nomes de todos os arquivos. Estou tentando pegar o mínimo possível de E / S do disco.

Eu experimentei alguns scripts shell e Perl sem sucesso. Alguma ideia?

ks1322
fonte
2
certifique-se de que seu "ls" seja / usr / bin / ls e não um alias para algo mais sofisticado.
21119 Glenn Jackman
Pergunta semelhante com respostas interessantes aqui: serverfault.com/questions/205071/…
aidan
Vale ressaltar que a maioria, se não todas, as soluções apresentadas para essa pergunta não são específicas para Linux , mas são bastante gerais para todos os sistemas * NIX-like. Talvez a remoção da tag "Linux" seja apropriada.
Christopher Schultz

Respostas:

189

Por padrão, lsclassifica os nomes, o que pode demorar um pouco, se houver muitos deles. Também não haverá saída até que todos os nomes sejam lidos e classificados. Use a ls -fopção para desativar a classificação.

ls -f | wc -l

Note-se que isso também vai permitir que -a, por isso ., ..e outros arquivos começando com .será contado.

mark4o
fonte
11
+1 E eu pensei que sabia tudo o que havia para saber ls.
15/09/09 mob
5
ZOMG. A classificação de 100 mil linhas não é nada - comparada com a stat()chamada lsem todos os arquivos. findNão faz stat()assim, ele funciona mais rápido.
Dummy00001
12
ls -ftambém não stat(). Mas é claro que ambos lse findchamar stat()quando certas opções são usadas, como ls -lou find -mtime.
mark4o
7
Por contexto, demorou de 1 a 2 minutos para contar 2,5 milhões de jpgs em uma pequena caixa Slicehost.
23411 philfreo
6
Se você quiser adicionar subdiretórios à contagem, façals -fR | wc -l
Ryan Walls
62

A maneira mais rápida é um programa criado para esse fim, como este:

#include <stdio.h>
#include <dirent.h>

int main(int argc, char *argv[]) {
    DIR *dir;
    struct dirent *ent;
    long count = 0;

    dir = opendir(argv[1]);

    while((ent = readdir(dir)))
            ++count;

    closedir(dir);

    printf("%s contains %ld files\n", argv[1], count);

    return 0;
}

Nos meus testes, sem considerar o cache, executei cada uma delas cerca de 50 vezes cada vez no mesmo diretório, repetidamente, para evitar a distorção dos dados baseados em cache, e obtive aproximadamente os seguintes números de desempenho (em tempo real):

ls -1  | wc - 0:01.67
ls -f1 | wc - 0:00.14
find   | wc - 0:00.22
dircnt | wc - 0:00.04

Esse último,, dircnté o programa compilado a partir da fonte acima.

EDIT 26-09-2016

Devido à demanda popular, reescrevi este programa para ser recursivo; portanto, ele cai em subdiretórios e continua a contar arquivos e diretórios separadamente.

Como fica claro que algumas pessoas querem saber como fazer tudo isso, tenho muitos comentários no código para tentar tornar óbvio o que está acontecendo. Eu escrevi e testei no Linux de 64 bits, mas deve funcionar em qualquer sistema compatível com POSIX, incluindo o Microsoft Windows. Relatórios de erros são bem-vindos; É um prazer atualizá-lo se você não conseguir fazê-lo funcionar no seu AIX ou OS / 400 ou o que for.

Como você pode ver, é muito mais complicado do que o original e necessariamente o mesmo: pelo menos uma função deve existir para ser chamada recursivamente, a menos que você queira que o código se torne muito complexo (por exemplo, gerenciando uma pilha de subdiretórios e processando-a em um único loop). Como temos que verificar os tipos de arquivo, as diferenças entre diferentes sistemas operacionais, bibliotecas padrão etc. entram em cena, por isso escrevi um programa que tenta ser utilizável em qualquer sistema em que ele seja compilado.

Há muito pouca verificação de erros, e a countprópria função realmente não relata erros. As únicas chamadas que realmente podem falhar são opendire stat(se você não tiver sorte e já possui um sistema que já direntcontém o tipo de arquivo). Eu não sou paranóico sobre como verificar o comprimento total dos caminhos de subdiretórios, mas, teoricamente, o sistema não deve permitir qualquer nome caminho que é maior do que do que PATH_MAX. Se houver preocupações, eu posso consertar isso, mas é apenas mais um código que precisa ser explicado para alguém aprendendo a escrever C. Este programa pretende ser um exemplo de como mergulhar nos subdiretórios recursivamente.

#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/stat.h>

#if defined(WIN32) || defined(_WIN32) 
#define PATH_SEPARATOR '\\' 
#else
#define PATH_SEPARATOR '/' 
#endif

/* A custom structure to hold separate file and directory counts */
struct filecount {
  long dirs;
  long files;
};

/*
 * counts the number of files and directories in the specified directory.
 *
 * path - relative pathname of a directory whose files should be counted
 * counts - pointer to struct containing file/dir counts
 */
void count(char *path, struct filecount *counts) {
    DIR *dir;                /* dir structure we are reading */
    struct dirent *ent;      /* directory entry currently being processed */
    char subpath[PATH_MAX];  /* buffer for building complete subdir and file names */
    /* Some systems don't have dirent.d_type field; we'll have to use stat() instead */
#if !defined ( _DIRENT_HAVE_D_TYPE )
    struct stat statbuf;     /* buffer for stat() info */
#endif

/* fprintf(stderr, "Opening dir %s\n", path); */
    dir = opendir(path);

    /* opendir failed... file likely doesn't exist or isn't a directory */
    if(NULL == dir) {
        perror(path);
        return;
    }

    while((ent = readdir(dir))) {
      if (strlen(path) + 1 + strlen(ent->d_name) > PATH_MAX) {
          fprintf(stdout, "path too long (%ld) %s%c%s", (strlen(path) + 1 + strlen(ent->d_name)), path, PATH_SEPARATOR, ent->d_name);
          return;
      }

/* Use dirent.d_type if present, otherwise use stat() */
#if defined ( _DIRENT_HAVE_D_TYPE )
/* fprintf(stderr, "Using dirent.d_type\n"); */
      if(DT_DIR == ent->d_type) {
#else
/* fprintf(stderr, "Don't have dirent.d_type, falling back to using stat()\n"); */
      sprintf(subpath, "%s%c%s", path, PATH_SEPARATOR, ent->d_name);
      if(lstat(subpath, &statbuf)) {
          perror(subpath);
          return;
      }

      if(S_ISDIR(statbuf.st_mode)) {
#endif
          /* Skip "." and ".." directory entries... they are not "real" directories */
          if(0 == strcmp("..", ent->d_name) || 0 == strcmp(".", ent->d_name)) {
/*              fprintf(stderr, "This is %s, skipping\n", ent->d_name); */
          } else {
              sprintf(subpath, "%s%c%s", path, PATH_SEPARATOR, ent->d_name);
              counts->dirs++;
              count(subpath, counts);
          }
      } else {
          counts->files++;
      }
    }

/* fprintf(stderr, "Closing dir %s\n", path); */
    closedir(dir);
}

int main(int argc, char *argv[]) {
    struct filecount counts;
    counts.files = 0;
    counts.dirs = 0;
    count(argv[1], &counts);

    /* If we found nothing, this is probably an error which has already been printed */
    if(0 < counts.files || 0 < counts.dirs) {
        printf("%s contains %ld files and %ld directories\n", argv[1], counts.files, counts.dirs);
    }

    return 0;
}

EDIT 2017-01-17

Incorporei duas alterações sugeridas por @FlyingCodeMonkey:

  1. Use em lstatvez de stat. Isso mudará o comportamento do programa se você tiver diretórios com links simbólicos no diretório que está digitalizando. O comportamento anterior era que o subdiretório (vinculado) teria sua contagem de arquivos adicionada à contagem geral; o novo comportamento é que o diretório vinculado contará como um único arquivo e seu conteúdo não será contado.
  2. Se o caminho de um arquivo for muito longo, uma mensagem de erro será emitida e o programa será interrompido.

EDIT 2017-06-29

Com alguma sorte, esta será a última edição desta resposta :)

Copiei esse código em um repositório do GitHub para facilitar um pouco a obtenção do código (em vez de copiar / colar, você pode simplesmente fazer o download do código-fonte ), além de facilitar a sugestão de uma modificação enviando um pull -requisição do GitHub.

A fonte está disponível sob a Licença Apache 2.0. Patches * bem-vindos!


  • "patch" é o que pessoas idosas como eu chamam de "solicitação pull".
Christopher Schultz
fonte
2
Apenas ótimo! obrigado! E para quem não sabe: você pode complile o código acima no terminal: gcc -o dircnt dircnt.ce uso é assim./dircnt some_dir
aesede
Existe uma maneira fácil de tornar isso recursivo?
ck_
@ck_ Claro, isso pode ser facilmente recursivo. Você precisa de ajuda com a solução ou quer que eu escreva tudo?
Christopher Schultz
1
@ChristopherSchultz, os benchmarks que você postou acima - qual era o tamanho do diretório em questão?
Dom Vinyard
1
Eu realmente queria usar isso em Python, então o empacotei como o pacote ffcount . Obrigado por disponibilizar o código @ChristopherSchultz!
GjjvdBurg
35

Você tentou encontrar? Por exemplo:

find . -name "*.ext" | wc -l
igustin
fonte
1
Isso encontrará recursivamente os arquivos no diretório atual.
Mark4o 15/09/09
No meu sistema, find /usr/share | wc -l(~ 137.000 arquivos) é cerca de 25% mais rápido que ls -R /usr/share | wc -l(~ 160.000 linhas, incluindo nomes de dir, totais de dir e linhas em branco) na primeira execução de cada uma e pelo menos duas vezes mais rápido ao comparar execuções subsequentes (armazenadas em cache).
Pausado até novo aviso.
11
Se ele quiser apenas o diretório atual, e não a árvore inteira recursivamente, ele poderá adicionar a opção -maxdepth 1 para encontrar.
igustin
3
Parece que o motivo findé mais rápido do que lsé por causa de como você está usando ls. Se você parar de classificar lse findtiver desempenho semelhante.
Christopher Schultz
17

find, ls e perl testados em 40.000 arquivos: a mesma velocidade (embora eu não tenha tentado limpar o cache):

[user@server logs]$ time find . | wc -l
42917

real    0m0.054s
user    0m0.018s
sys     0m0.040s
[user@server logs]$ time /bin/ls -f | wc -l
42918

real    0m0.059s
user    0m0.027s
sys     0m0.037s

e com perl opendir / readdir, ao mesmo tempo:

[user@server logs]$ time perl -e 'opendir D, "."; @files = readdir D; closedir D; print scalar(@files)."\n"'
42918

real    0m0.057s
user    0m0.024s
sys     0m0.033s

observação: usei / bin / ls -f para ignorar a opção de alias, que pode diminuir um pouco e -f para evitar a ordenação de arquivos. ls sem -f é duas vezes mais lento que o find / perl, exceto se ls for usado com -f, parece que é o mesmo tempo:

[user@server logs]$ time /bin/ls . | wc -l
42916

real    0m0.109s
user    0m0.070s
sys     0m0.044s

Eu também gostaria de ter algum script para perguntar diretamente ao sistema de arquivos sem todas as informações desnecessárias.

testes baseados na resposta de Peter van der Heijden, glenn jackman e mark4o.

Thomas

Thomas
fonte
5
Você definitivamente deve limpar o cache entre os testes. A primeira vez que executo ls -l | wc -luma pasta em um HDD externo de 2,5 "com arquivos de 1M, leva cerca de 3 minutos para a operação terminar. Na segunda vez, leva 12 segundos IIRC. Além disso, isso também pode depender do seu sistema de arquivos. I estava usando Btrfs.
Behrang Saeedzadeh
Obrigado, o snippet perl é a solução para mim. $ time perl -e 'opendir D, "."; @files = readdir D; closedir D; print scalar(@files)."\n"' 1315029 real 0m0.580s user 0m0.302s sys 0m0.275s
Pažout 15/06
5

Você pode alterar a saída com base em seus requisitos, mas aqui está uma linha que eu escrevi para contar recursivamente e informar o número de arquivos em uma série de diretórios numerados.

dir=/tmp/count_these/ ; for i in $(ls -1 ${dir} | sort -n) ; { echo "$i => $(find ${dir}${i} -type f | wc -l),"; }

Ele procura recursivamente todos os arquivos (não diretórios) no diretório especificado e retorna os resultados em um formato semelhante a hash. Ajustes simples no comando find podem tornar o tipo de arquivo que você deseja contar mais específico, etc.

Resultados em algo como isto:

1 => 38,
65 => 95052,
66 => 12823,
67 => 10572,
69 => 67275,
70 => 8105,
71 => 42052,
72 => 1184,
mightybs
fonte
1
Achei o exemplo um pouco confuso. Fiquei me perguntando por que havia números à esquerda, em vez de nomes de diretório. Obrigado por isso, porém, acabei usando-o com alguns pequenos ajustes. (contando diretórios e eliminando o nome da pasta base. for i em $ (ls -1. | sort -n); {echo "$ i => $ (encontre $ {i} | wc -l)";}
TheJacobTaylor
Os números à esquerda são meus nomes de diretório dos meus dados de exemplo. Desculpe, isso foi confuso.
mightybs
1
ls -1 ${dir}não funcionará corretamente sem mais espaços. Além disso, não há garantia de que o nome retornado por lspossa ser passado find, pois lsevita caracteres não imprimíveis para consumo humano. ( mkdir $'oddly\nnamed\ndirectory'se você quiser um caso de teste particularmente interessante). Veja Por que você não deve analisar a saída de ls (1)
Charles Duffy
4

Surpreendentemente para mim, uma descoberta simples é muito comparável a ls -f

> time ls -f my_dir | wc -l
17626

real    0m0.015s
user    0m0.011s
sys     0m0.009s

versus

> time find my_dir -maxdepth 1 | wc -l
17625

real    0m0.014s
user    0m0.008s
sys     0m0.010s

Obviamente, os valores na terceira casa decimal mudam um pouco toda vez que você executa um desses, então são basicamente idênticos. Observe, no entanto, que findretorna uma unidade extra, porque conta o próprio diretório real (e, como mencionado anteriormente, ls -fretorna duas unidades extras, pois também conta. E ..).

Bogdan Stăncescu
fonte
4

Apenas adicionando isso por uma questão de integridade. A resposta correta, obviamente, já foi postada por outra pessoa, mas você também pode obter uma contagem de arquivos e diretórios com o programa em árvore.

Execute o comando tree | tail -n 1para obter a última linha, que dirá algo como "763 diretórios, arquivos 9290". Isso conta arquivos e pastas recursivamente, excluindo arquivos ocultos, que podem ser adicionados com o sinalizador -a. Para referência, levou 4,8 segundos no meu computador para que a árvore contasse todo o diretório doméstico, que era 24777 diretórios, 238680 arquivos. find -type f | wc -ldemorou 5,3 segundos, meio segundo a mais, então acho que a árvore é bastante competitiva em termos de velocidade.

Contanto que você não tenha subpastas, o tree é uma maneira rápida e fácil de contar os arquivos.

Além disso, e apenas por diversão, você pode usar tree | grep '^├'para mostrar apenas os arquivos / pastas no diretório atual - esta é basicamente uma versão muito mais lenta do ls.

Benubird
fonte
Brew install tailpara OS X.
The Unfun Cat
O @TheUnfunCat tailjá deve estar instalado no seu sistema Mac OS X.
Christopher Schultz
4

Contagem rápida de arquivos do Linux

A contagem mais rápida de arquivos linux que conheço é

locate -c -r '/home'

Não necessidade de chamar grep! Mas, como mencionado, você deve ter um novo banco de dados (atualizado diariamente por um trabalho cron ou manual por sudo updatedb).

De man localizar

-c, --count
    Instead  of  writing  file  names on standard output, write the number of matching
    entries only.

Além disso, você deve saber que também conta os diretórios como arquivos!


BTW: Se você quiser uma visão geral dos seus arquivos e diretórios no seu sistema, digite

locate -S

Ele gera o número de diretórios, arquivos etc.

abu_bua
fonte
nota que você tem que ter certeza de que o banco de dados é up-to-date
phuclv
1
LOL Se você já tiver todas as contagens em um banco de dados, certamente poderá contar rapidamente. :)
Christopher Schultz
3

Escrevendo isso aqui, pois não tenho pontos de reputação suficientes para comentar uma resposta, mas estou autorizado a deixar minha própria resposta, o que não faz sentido. De qualquer forma...

Em relação à resposta de Christopher Schultz , sugiro alterar stat para lstat e possivelmente adicionar uma verificação de limites para evitar o estouro de buffer:

if (strlen(path) + strlen(PATH_SEPARATOR) + strlen(ent->d_name) > PATH_MAX) {
    fprintf(stdout, "path too long (%ld) %s%c%s", (strlen(path) + strlen(PATH_SEPARATOR) + strlen(ent->d_name)), path, PATH_SEPARATOR, ent->d_name);
    return;
}

A sugestão para usar lstat é evitar seguir links simbólicos que podem levar a ciclos se um diretório contiver um link simbólico para um diretório pai.

FlyingCodeMonkey
fonte
2
Modificar porque o uso de lstatera uma boa sugestão e você merece carma por isso. Essa sugestão foi incorporada ao meu código postado acima e, agora, no GitHub.
22417 Christopher Schultz
2

Você poderia tentar se usar opendir()e readdir()em Perlé mais rápido. Para um exemplo dessas funções, veja aqui

Peter van der Heijden
fonte
2
uso: perl -e 'opendir D, "."; @files = readdir D; fechado D; print scalar (@files) '
glenn jackman
2

Esta resposta aqui é mais rápida do que quase tudo nesta página para diretórios muito grandes e muito aninhados:

https://serverfault.com/a/691372/84703

locate -r '.' | grep -c "^$PWD"

ck_
fonte
1
Agradável. Como você já possui um banco de dados atualizado de todos os arquivos, não há necessidade de fazê-lo novamente. Mas, infelizmente, você deve verificar se o comando updatedb já foi executado e concluído para este método.
Chris Reid
você não precisa grep. Use locate -c -r '/path'como na solução de
abu_bua
2

Eu vim aqui ao tentar contar os arquivos em um conjunto de dados de ~ 10K pastas com ~ 10K arquivos cada. O problema de muitas das abordagens é que elas implicitamente registram arquivos de 100 milhões, o que leva idades.

Tomei a liberdade de estender a abordagem de christopher-schultz, para que ele suporte a passagem de diretórios via args (sua abordagem recursiva também usa stat).

Coloque o seguinte no arquivo dircnt_args.c:

#include <stdio.h>
#include <dirent.h>

int main(int argc, char *argv[]) {
    DIR *dir;
    struct dirent *ent;
    long count;
    long countsum = 0;
    int i;

    for(i=1; i < argc; i++) {
        dir = opendir(argv[i]);
        count = 0;
        while((ent = readdir(dir)))
            ++count;

        closedir(dir);

        printf("%s contains %ld files\n", argv[i], count);
        countsum += count;
    }
    printf("sum: %ld\n", countsum);

    return 0;
}

Depois de um, gcc -o dircnt_args dircnt_args.cvocê pode invocá-lo assim:

dircnt_args /your/dirs/*

Em arquivos de 100 milhões de pastas com 10 mil cópias, o processo acima é concluído rapidamente (~ 5 minutos para a primeira execução, acompanhamento no cache: ~ 23 s).

A única outra abordagem que terminou em menos de uma hora foi sl com cerca de 1 min em cache de: ls -f /your/dirs/* | wc -l. A contagem está desativada em algumas linhas novas por dir ...

Além do esperado, nenhuma das minhas tentativas findretornou em uma hora: - /

Jörn Hees
fonte
Para alguém que não é um programador C, você pode explicar por que isso seria mais rápido e como é possível obter a mesma resposta sem fazer a mesma coisa?
Mlissner
você não precisa ser um programador C, apenas entenda o que significa estatizar um arquivo e como os diretórios são representados: os diretórios são essencialmente listas de nomes de arquivos e inodes. Se você especificar um arquivo, acesse o inode que está em algum lugar na unidade para obter, por exemplo, informações como tamanho do arquivo, permissões, .... Se você está interessado apenas nas contagens por diretório, não precisa acessar as informações do inode, o que pode economizar muito tempo.
Jörn Hees
Este segfaults no Oracle Linux, versão gcc 4.8.5 20.150.623 (Red Hat 4.8.5-28.0.1) (GCC) ... caminhos relativos e fs de remotas parecem ser a causa
Rondo
2

A maneira mais rápida no linux (a questão é marcada como linux) é usar a chamada direta do sistema. Aqui está um pequeno programa que conta arquivos (apenas, sem diretórios) em um diretório. Você pode contar milhões de arquivos e é cerca de 2,5 vezes mais rápido que "ls -f" e 1,3 a 1,5 vezes mais rápido que a resposta de Christopher Schultz.

#define _GNU_SOURCE
#include <dirent.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/syscall.h>

#define BUF_SIZE 4096

struct linux_dirent {
    long d_ino;
    off_t d_off;
    unsigned short d_reclen;
    char d_name[];
};

int countDir(char *dir) {


    int fd, nread, bpos, numFiles = 0;
    char d_type, buf[BUF_SIZE];
    struct linux_dirent *dirEntry;

    fd = open(dir, O_RDONLY | O_DIRECTORY);
    if (fd == -1) {
        puts("open directory error");
        exit(3);
    }
    while (1) {
        nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);
        if (nread == -1) {
            puts("getdents error");
            exit(1);
        }
        if (nread == 0) {
            break;
        }

        for (bpos = 0; bpos < nread;) {
            dirEntry = (struct linux_dirent *) (buf + bpos);
            d_type = *(buf + bpos + dirEntry->d_reclen - 1);
            if (d_type == DT_REG) {
                // Increase counter
                numFiles++;
            }
            bpos += dirEntry->d_reclen;
        }
    }
    close(fd);

    return numFiles;
}

int main(int argc, char **argv) {

    if (argc != 2) {
        puts("Pass directory as parameter");
        return 2;
    }
    printf("Number of files in %s: %d\n", argv[1], countDir(argv[1]));
    return 0;
}

PS: Não é recursivo, mas você pode modificá-lo para conseguir isso.

Nikolay Dimitrov
fonte
1
Não sei se concordo que isso é mais rápido. Não rastreei tudo o que o compilador faz com opendir/ readdir, mas suspeito que tudo se resume ao mesmo código no final. Fazer chamadas do sistema dessa maneira também não é portátil e, como a ABI do Linux não é estável, não é garantido que um programa compilado em um sistema funcione corretamente em outro (embora seja um bom conselho compilar qualquer coisa da fonte em qualquer sistema * NIX IMO ) Se a velocidade é fundamental, esta é uma boa solução se ela realmente melhorar a velocidade - eu não comparei os programas separadamente.
21417 Christopher Schultz
1

lsgasta mais tempo classificando os nomes dos arquivos, usar -fpara desativar a classificação economizará algum tempo:

ls -f | wc -l

ou você pode usar find:

find . -type f | wc -l
Mohammad Anini
fonte
0

Percebi que não usar no processamento de memória quando você tem uma quantidade enorme de dados é mais rápido do que "canalizar" os comandos. Então eu salvei o resultado em um arquivo e depois o analisei

ls -1 /path/to/dir > count.txt && cat count.txt | wc -l
Marcelo Luiz Onhate
fonte
essa não é a solução mais rápida porque os discos rígidos são extremamente lentos. Há outras maneiras mais eficientes que foram postadas anos antes de você
phuclv
0

Você deve usar "getdents" no lugar de ls / find

Aqui está um artigo muito bom que descreveu a abordagem getdents.

http://be-n.com/spw/you-can-list-a-million-files-in-a-directory-but-not-with-ls.html

Aqui está o extrato:

sl e praticamente todos os outros métodos para listar um diretório (incluindo python os.listdir, find.) dependem da libc readdir (). No entanto, readdir () lê apenas 32K de entradas de diretório por vez, o que significa que se você tiver muitos arquivos no mesmo diretório (por exemplo, 500M de entradas de diretório), levará um tempo incrivelmente longo para ler todas as entradas de diretório , especialmente em um disco lento. Para diretórios que contêm um grande número de arquivos, você precisará ir além das ferramentas que dependem de readdir (). Você precisará usar o syscall getdents () diretamente, em vez dos métodos auxiliares da libc.

Podemos encontrar o código C para listar os arquivos usando getdents () a partir daqui :

Há duas modificações que você precisará fazer para listar rapidamente todos os arquivos em um diretório.

Primeiro, aumente o tamanho do buffer de X para algo como 5 megabytes.

#define BUF_SIZE 1024*1024*5

Em seguida, modifique o loop principal onde ele imprime as informações sobre cada arquivo no diretório para ignorar entradas com inode == 0. Fiz isso adicionando

if (dp->d_ino != 0) printf(...);

No meu caso, também me importei apenas com os nomes de arquivo no diretório, então também reescrevi a instrução printf () para imprimir apenas o nome do arquivo.

if(d->d_ino) printf("%sn ", (char *) d->d_name);

Compile-o (ele não precisa de bibliotecas externas, por isso é super simples de fazer)

gcc listdir.c -o listdir

Agora apenas corra

./listdir [directory with insane number of files]
Dev123
fonte
Observe que o Linux faz uma leitura antecipada, portanto readdir()não é realmente lento. Preciso de uma figura sólida antes de acreditar que vale a pena jogar fora a portabilidade para esse ganho de desempenho.
fuz 12/05/19
-1

Prefiro o seguinte comando para acompanhar as alterações no número de arquivos em um diretório.

watch -d -n 0.01 'ls | wc -l'

O comando manterá uma janela aberta para rastrear o número de arquivos que estão no diretório com uma taxa de atualização de 0,1 s.

Anoop Toffy
fonte
tem certeza de que ls | wc -lterminará em uma pasta com milhares ou milhões de arquivos em 0,01s? até o seu lsé extremamente ineficiente em comparação com outras soluções. E o OP quer apenas obter a contagem, não sentado lá olhando para a saída mudando
phuclv
Bem. Bem. Encontrei uma solução elegante que funciona para mim. Gostaria de compartilhar o mesmo. Eu não sei o comando 'ls' no linux é altamente ineficiente. O que você está usando em vez disso? E 0,01s é a taxa de atualização. Não é a hora. se você não usou o watch, consulte as páginas de manual.
Anoop Toffy
bem, eu li o watchmanual após esse comentário e vi que 0,01s (não 0,1s) é um número irreal, porque a taxa de atualização da maioria das telas de PC é de apenas 60Hz, e isso não responde à pergunta de forma alguma. O OP perguntou sobre "Contagem rápida de arquivos do Linux para um grande número de arquivos". Você também não leu nenhuma resposta disponível antes de postar
phuclv
Eu li as respostas. Mas o que eu postei é uma maneira de acompanhar a alteração do número de arquivos em um diretório. por exemplo: ao copiar um arquivo de um local para outro, o número de arquivos mantém as alterações. com o método que eu pôster, podemos acompanhar isso. Concordo que a postagem que eu não fiz não modifica ou melhora as postagens anteriores.
Anoop Toffy
-2

Os primeiros 10 diretórios com o maior número de arquivos.

dir=/ ; for i in $(ls -1 ${dir} | sort -n) ; { echo "$(find ${dir}${i} \
    -type f | wc -l) => $i,"; } | sort -nr | head -10
user2546874
fonte
3
Isso certamente se assemelha surpreendentemente à resposta (com os mesmos erros) escrita por mightybs . Se você deseja estender ou modificar o código escrito por outra pessoa, creditar é apropriado. Compreender o código que você está usando em suas respostas o suficiente para identificar e corrigir seus erros é ainda mais apropriado.
Charles Duffy