desenv-web-rp.com

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

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

73
Ramesh

ls realmente 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 no link this , podemos usar 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 pesquisar um pouco, descobri que, se listarmos os diretórios usando getdents(), é suposto ser mais rápido, porque ls, find e Python bibliotecas usam readdir(), que é mais lenta, mas usa getdents() embaixo.

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 tempos : getdents pode ser muito mais rápido que ls -f, dependendo da configuração do sistema. Aqui estão alguns horários que demonstram um aumento de velocidade de 40x para listar um diretório que contém 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, depois ls -f. A primeira execução é significativamente mais lenta que todas as outras, provavelmente devido a falhas na página de cache do NFS. (Além disso: sobre esta montagem, o d_type campo não é confiável, no sentido de que muitos arquivos aparecem como do 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%
49
Ramesh

Evite classificar usando:

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

Ou equivalente:

ls -U
71
Hauke Laging

A razão mais provável pela qual é lento é a coloração do tipo de arquivo. Você pode evitar isso com \ls ou /bin/ls desativando as opções de cores.

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

12
Alex Lehmann