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


70

J'avais un répertoire qui contenait environ 5 millions de fichiers. Lorsque j'ai essayé d'exécuter la lscommande à 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 lscommande?


11
Assurez-vous de ne pas avoir d'alias pour lscet usage --colorou -Fpour signifier que vous feriez un lstat(2)pour chaque fichier.
Stéphane Chazelas

4
À propos, stocker des millions de fichiers dans un seul répertoire est une très mauvaise idée. Si vous contrôlez la disposition du répertoire, peut-être la divisez-vous selon certains critères?
d33tah

Était-ce un pur lsappel ou avez-vous utilisé des options?
Hauke ​​Laging

1
@ d33tah Ouais, 5 millions, c'est beaucoup! Mon système de fichiers racine a une limite de 7 millions d'inodes.
Mikel

7
5 millions d'éléments à afficher - comment regardez-vous ceci - une simple liste est trop difficile à voir - alors que voulez-vous que la liste soit?
user151019

Réponses:


66

Évitez de trier en utilisant:

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

Ou équivalent:

ls -U

10
Je me demande combien de temps supplémentaire la disposition des colonnes ajoute également. L'ajout du -1drapeau pourrait aider.
Mikel

Probablement pas beaucoup, mais chaque petit geste compte, non? :)
Mikel

1
@ Mikel Est-ce juste une supposition, ou avez-vous mesuré cela? Il me semble que cela -1prend encore plus longtemps.
Hauke ​​Laging

10
"-1" aide un peu. "ls -f -1" évitera tout appel à stat et imprimera tout immédiatement. La sortie de colonne (qui est la valeur par défaut lors de l'envoi à un terminal) fait que tout est mis en tampon en premier. Sur mon système, si vous utilisez btrfs dans un répertoire de 8 millions de fichiers (créé par "seq 1 8000000 | xargs touch"), "time ls -f -1 | wc -l" prend moins de 5 secondes, tandis que "time ls -f -C | wc -l "prend plus de 30 secondes.
Scott Lamb

1
@ToolmakerSteve Le comportement par défaut ( -Cquand stdout est un terminal, -1quand il s'agit d'un tube) est déroutant. Lorsque vous expérimentez et mesurez, vous basculez entre afficher la sortie (pour vous assurer que la commande fonctionne comme vous le souhaitez) et la supprimer (pour éviter le facteur de confusion du débit de l'application de terminal). Il vaut mieux utiliser les commandes qui se comportent de la même manière dans les deux modes, définir explicitement le format de sortie via -1, -C, -l, etc.
Scott Lamb

47

lstrie réellement les fichiers et essaie de les lister, ce qui devient un lourd fardeau si nous essayons de répertorier plus d’un million de fichiers dans un répertoire. Comme mentionné dans ce lien, nous pouvons utiliser straceou findrépertorier les fichiers. Cependant, ces options semblaient également irréalisables pour mon problème puisque j'avais 5 millions de fichiers. Après un peu de recherche sur Google, j'ai trouvé que si nous listons les répertoires en utilisant getdents(), il est supposé être plus rapide, car ls, findet les Pythonbibliothèques utilisent readdir()ce qui est plus lent mais utilise en getdents()dessous.

Nous pouvons trouver le code C à la liste des fichiers à l' aide getdents()d' 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 listés. Puis exécutez les commandes ci-dessous.

gcc  getdents.c -o getdents
./getdents

Exemple de minutage : getdentspeut être beaucoup plus rapide que ls -f, en fonction de la configuration du système. Voici quelques timings démontrant une augmentation de 40 fois la vitesse de création d'un répertoire contenant environ 500 000 fichiers sur un montage NFS dans un cluster de calcul. Chaque commande a été exécutée 10 fois en succession immédiate, d’abord getdents, puis ls -f. La première exécution est nettement plus lente que toutes les autres, probablement en raison de défauts de mise en mémoire cache NFS. (En outre: sur ce montage, le d_typechamp n'est pas fiable, en ce sens que de nombreux fichiers apparaissent sous le 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%

14
Pourriez-vous ajouter un petit repère dans le calendrier pour lequel votre cas s'affiche avec ls?
Bernhard

1
Sucré. Et vous pouvez ajouter une option permettant de compter simplement les entrées (fichiers) plutôt que de lister leurs noms (enregistrement de millions d'appels sur printf pour cette liste).
ChuckCottrill

29
Vous savez que votre répertoire est trop volumineux lorsque vous devez écrire un code personnalisé pour en lister le contenu ...
casey

1
@ Casey Sauf que vous n'êtes pas obligé. Tout ce discours sur getdentsvs readdirmanque le point.
Mikel

9
Allons! Il contient déjà 5 millions de fichiers. Mettez votre programme "ls" personnalisé dans un autre répertoire.
Johan

12

Le plus probable raison pour laquelle il est lent est colorant de type de fichier, vous pouvez éviter cela avec \lsou de /bin/lsdésactiver les options de couleur.

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


7
Je ne pense pas que cela aurait dû être voté. Le tri est un problème, mais même sans tri, ls -U --colorcela prendrait beaucoup de temps, car statchaque fichier serait traité. Donc les deux sont corrects.
Mikel

La désactivation de la coloration a un impact considérable sur les performances lset est alias par défaut dans de nombreuses autres applications .bashrc.
Victor Schröder

Ouais j'ai fait une /bin/ls -Usortie et je l'ai obtenue en un rien de temps, comparé à attendre très longtemps avant
khebbie

-3

Je trouve que ça echo *marche beaucoup plus vite que ls. YMMV.


4
La coquille va trier le *. Donc, cette manière est probablement encore très lente pour 5 millions de fichiers.
Mikel

3
@ Mikel Plus que cela, je suis à peu près sûr que 5 millions de fichiers dépassent le point où la suppression sera complètement interrompue.
Evilsoup

4
La longueur minimale d'un nom de fichier (pour 5 millions de fichiers) est de 3 caractères (peut-être 4 si vous vous en tenez à des caractères plus communs) plus les délimiteurs = 4 caractères par fichier, soit 20 Mo d'arguments de commande. Cela dépasse largement la longueur de ligne de commande étendue de 2 Mo commune. Exec (et même les buildins) seraient en panne.
Johan
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.