web-dev-qa-db-fra.com

La commande ls ne fonctionne pas pour un répertoire contenant un grand nombre de fichiers

J'avais un répertoire qui contenait environ 5 millions de fichiers. Lorsque j'ai essayé d'exécuter la commande ls depuis l'intérieur de ce répertoire, mon système a consommé une énorme quantité de mémoire et s'est bloqué après un certain temps. Existe-t-il un moyen efficace de répertorier les fichiers autrement qu'en utilisant la commande ls?

73
Ramesh

ls trie en fait les fichiers et essaie de les lister, ce qui devient une énorme surcharge si nous essayons de lister plus d'un million de fichiers dans un répertoire. Comme mentionné dans le lien this , nous pouvons utiliser strace ou find pour lister les fichiers. Cependant, ces options semblaient également irréalisables à mon problème puisque j'avais 5 millions de fichiers. Après quelques recherches sur Google, j'ai constaté que si nous répertorions les répertoires à l'aide de getdents(), cela est censé être plus rapide, car ls, find et Python les bibliothèques utilisent readdir() qui est plus lente mais utilise getdents() en dessous.

Nous pouvons trouver le code C pour lister les fichiers en utilisant getdents() de ici :

/*
 * 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);
}

Copiez le programme C ci-dessus dans le répertoire dans lequel les fichiers doivent être répertoriés. Exécutez ensuite les commandes ci-dessous.

gcc  getdents.c -o getdents
./getdents

Exemple de timing : getdents peut être beaucoup plus rapide que ls -f, selon la configuration du système. Voici quelques timings démontrant une augmentation de la vitesse 40x pour lister un répertoire contenant environ 500k fichiers sur un montage NFS dans un cluster de calcul. Chaque commande a été exécutée 10 fois de suite, d'abord getdents, puis ls -f. La première exécution est beaucoup plus lente que toutes les autres, probablement en raison de défauts de page de mise en cache NFS. (À part: sur cette monture, le d_type champ n'est pas fiable, dans le sens où de nombreux fichiers apparaissent de type "inconnu".)

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

Évitez de trier en utilisant:

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

Ou équivalent:

ls -U
71
Hauke Laging

La raison la plus probable pour laquelle il est lent est la coloration du type de fichier, vous pouvez éviter cela avec \ls ou /bin/ls désactiver les options de couleur.

Si vous avez vraiment autant de fichiers dans un répertoire, utiliser find à la place est également une bonne option.

12
Alex Lehmann