O comando ls não está funcionando para um diretório com um grande número de arquivos

60

Eu tinha um diretório que tinha cerca de 5 milhões arquivos. Quando tentei executar o comando ls dentro desse diretório, meu sistema consumiu uma grande quantidade de memória e ficou pendurado depois de algum tempo. Existe uma maneira eficiente de listar os arquivos além de usar o comando ls ?

    
por Ramesh 17.03.2014 / 17:00

4 respostas

40

ls na verdade classifica os arquivos e tenta listá-los, o que se torna uma enorme sobrecarga se estivermos tentando listar mais de um milhão de arquivos dentro de um diretório. Como mencionado em este link , podemos use strace ou find para listar os arquivos. No entanto, essas opções também pareciam inviáveis para o meu problema, pois eu tinha 5 milhões de arquivos. Depois de um pouco de googling, descobri que se listarmos os diretórios usando getdents() , é suposto ser mais rápido, porque as bibliotecas ls , find e Python usam readdir() , que é mais lento, mas usa getdents() por baixo.

Podemos encontrar o código C para listar os arquivos usando getdents() de aqui :

/*
 * List directories using getdents() because ls, find and Python libraries
 * use readdir() which is slower (but uses getdents() underneath.
 *
 * Compile with 
 * ]$ gcc  getdents.c -o getdents
 */
#define _GNU_SOURCE
#include <dirent.h>     /* Defines DT_* constants */
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/syscall.h>

#define handle_error(msg) \
       do { perror(msg); exit(EXIT_FAILURE); } while (0)

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

#define BUF_SIZE 1024*1024*5

int
main(int argc, char *argv[])
{
   int fd, nread;
   char buf[BUF_SIZE];
   struct linux_dirent *d;
   int bpos;
   char d_type;

   fd = open(argc > 1 ? argv[1] : ".", O_RDONLY | O_DIRECTORY);
   if (fd == -1)
       handle_error("open");

   for ( ; ; ) {
       nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);
       if (nread == -1)
           handle_error("getdents");

       if (nread == 0)
           break;

       for (bpos = 0; bpos < nread;) {
           d = (struct linux_dirent *) (buf + bpos);
           d_type = *(buf + bpos + d->d_reclen - 1);
           if( d->d_ino != 0 && d_type == DT_REG ) {
              printf("%s\n", (char *)d->d_name );
           }
           bpos += d->d_reclen;
       }
   }

   exit(EXIT_SUCCESS);
}

Copie o programa C acima no diretório em que os arquivos precisam ser listados. Em seguida, execute os comandos abaixo.

gcc  getdents.c -o getdents
./getdents

Exemplo de intervalos : getdents pode ser muito mais rápido que ls -f , dependendo da configuração do sistema. Aqui estão alguns tempos demonstrando um aumento de velocidade de 40x para listar um diretório contendo cerca de 500k arquivos em uma montagem NFS em um cluster de computação. Cada comando foi executado 10 vezes em sucessão imediata, primeiro getdents e, em seguida, ls -f . A primeira execução é significativamente mais lenta que todas as outras, provavelmente devido a falhas de página de cache do NFS. (Além: sobre essa montagem, o campo d_type não é confiável, no sentido de que muitos arquivos aparecem como tipo "desconhecido".)

command: getdents $bigdir
usr:0.08 sys:0.96  wall:280.79 CPU:0%
usr:0.06 sys:0.18  wall:0.25   CPU:97%
usr:0.05 sys:0.16  wall:0.21   CPU:99%
usr:0.04 sys:0.18  wall:0.23   CPU:98%
usr:0.05 sys:0.20  wall:0.26   CPU:99%
usr:0.04 sys:0.18  wall:0.22   CPU:99%
usr:0.04 sys:0.17  wall:0.22   CPU:99%
usr:0.04 sys:0.20  wall:0.25   CPU:99%
usr:0.06 sys:0.18  wall:0.25   CPU:98%
usr:0.06 sys:0.18  wall:0.25   CPU:98%
command: /bin/ls -f $bigdir
usr:0.53 sys:8.39  wall:8.97   CPU:99%
usr:0.53 sys:7.65  wall:8.20   CPU:99%
usr:0.44 sys:7.91  wall:8.36   CPU:99%
usr:0.50 sys:8.00  wall:8.51   CPU:100%
usr:0.41 sys:7.73  wall:8.15   CPU:99%
usr:0.47 sys:8.84  wall:9.32   CPU:99%
usr:0.57 sys:9.78  wall:10.36  CPU:99%
usr:0.53 sys:10.75 wall:11.29  CPU:99%
usr:0.46 sys:8.76  wall:9.25   CPU:99%
usr:0.50 sys:8.58  wall:9.13   CPU:99%
    
por 17.03.2014 / 17:00
60

Evite classificar usando:

ls --sort=none # "do not sort; list entries in directory order"

Ou, de forma equivalente:

ls -U
    
por 17.03.2014 / 17:09
9

O motivo mais provável para isso é a cor do tipo de arquivo, você pode evitar isso com \ls ou /bin/ls desativando as opções de cor.

Se você realmente tem muitos arquivos em um diretório, usar find também é uma boa opção.

    
por 18.03.2014 / 13:48
-3

Acho que echo * funciona muito mais rápido que o ls. YMMV.

    
por 18.03.2014 / 14:39

Tags