Compte de fichiers Linux rapide pour un grand nombre de fichiers


137

J'essaie de trouver le meilleur moyen de trouver le nombre de fichiers dans un répertoire particulier lorsqu'il y a un très grand nombre de fichiers (> 100 000).

Lorsqu'il y a autant de fichiers, l'exécution ls | wc -lprend beaucoup de temps. Je pense que c'est parce qu'il renvoie les noms de tous les fichiers. J'essaie de prendre le moins possible d'E / S disque.

J'ai essayé en vain des scripts shell et Perl. Des idées?


2
assurez-vous que votre "ls" est / usr / bin / ls et non un alias de quelque chose de plus sophistiqué.
glenn jackman

Question similaire avec des réponses intéressantes ici: serverfault.com/questions/205071/…
aidan

Il vaut la peine de souligner que la plupart sinon toutes les solutions présentées à cette question ne sont pas spécifiques à Linux , mais sont assez générales pour tous les systèmes de type * NIX. Peut-être que la suppression de la balise «Linux» est appropriée.
Christopher Schultz

Réponses:


189

Par défaut, lstrie les noms, ce qui peut prendre un certain temps s'il y en a beaucoup. De plus, il n'y aura pas de sortie tant que tous les noms ne seront pas lus et triés. Utilisez l' ls -foption pour désactiver le tri.

ls -f | wc -l

Notez que cela permettra aussi -a, donc ., ..et d' autres fichiers commençant par .seront comptés.


11
+1 Et je pensais que je savais tout ce qu'il y avait à savoir ls.
mob

5
ZOMG. Le tri de 100 000 lignes n'est rien - comparé à l' stat()appel lsfait sur chaque fichier. findne fonctionne stat()donc pas plus vite.
Dummy00001

12
ls -fnon stat()plus. Mais bien sûr les deux lset findcall stat()lorsque certaines options sont utilisées, telles que ls -lou find -mtime.
mark4o

7
Pour le contexte, cela a pris 1 à 2 minutes pour compter 2,5 millions de jpgs sur une petite boîte Slicehost.
philfreo

6
Si vous voulez ajouter des sous-répertoires au décompte, faitesls -fR | wc -l
Ryan Walls

62

Le moyen le plus rapide est un programme spécialement conçu, comme celui-ci:

#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;
}

De mes tests sans égard au cache, j'ai exécuté chacun de ces environ 50 fois chacun sur le même répertoire, encore et encore, pour éviter le biais des données basées sur le cache, et j'ai obtenu à peu près les chiffres de performance suivants (en temps réel):

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

Ce dernier,, dircntest le programme compilé à partir de la source ci-dessus.

MODIFIER 2016-09-26

En raison de la demande générale, j'ai réécrit ce programme pour qu'il soit récursif, il tombera donc dans les sous-répertoires et continuera à compter les fichiers et les répertoires séparément.

Comme il est clair que certaines personnes veulent savoir comment faire tout cela, j'ai beaucoup de commentaires dans le code pour essayer de rendre évident ce qui se passe. Je l'ai écrit et testé sur Linux 64 bits, mais cela devrait fonctionner sur n'importe quel système compatible POSIX, y compris Microsoft Windows. Les rapports de bogue sont les bienvenus; Je suis heureux de mettre à jour ceci si vous ne pouvez pas le faire fonctionner sur votre AIX ou OS / 400 ou autre.

Comme vous pouvez le voir, c'est beaucoup plus compliqué que l'original et forcément: au moins une fonction doit exister pour être appelée de manière récursive à moins que vous ne souhaitiez que le code devienne très complexe (par exemple gérer une pile de sous-répertoires et traiter cela en une seule boucle). Comme nous devons vérifier les types de fichiers, les différences entre les différents systèmes d'exploitation, les bibliothèques standard, etc. entrent en jeu, j'ai donc écrit un programme qui essaie d'être utilisable sur n'importe quel système où il compilera.

Il y a très peu de vérification des erreurs et la countfonction elle-même ne signale pas vraiment les erreurs. Les seuls appels qui peuvent vraiment échouer sont opendiret stat(si vous n'avez pas de chance et que votre système direntcontient déjà le type de fichier). Je ne suis pas paranoïaque à propos de la vérification de la longueur totale des chemins des sous-répertoires, mais théoriquement, le système ne devrait autoriser aucun nom de chemin plus long que PATH_MAX. S'il y a des soucis, je peux résoudre ça, mais c'est juste plus de code qui doit être expliqué à quelqu'un qui apprend à écrire C. Ce programme est destiné à être un exemple de la façon de plonger dans les sous-répertoires de manière récursive.

#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;
}

MODIFIER 2017-01-17

J'ai incorporé deux changements suggérés par @FlyingCodeMonkey:

  1. Utilisez à la lstatplace de stat. Cela changera le comportement du programme si vous avez des répertoires liés par un lien symbolique dans le répertoire que vous analysez. Le comportement précédent était que le sous-répertoire (lié) verrait son nombre de fichiers ajouté au nombre global; le nouveau comportement est que le répertoire lié comptera comme un seul fichier et que son contenu ne sera pas compté.
  2. Si le chemin d'un fichier est trop long, un message d'erreur sera émis et le programme s'arrêtera.

MODIFIER 2017-06-29

Avec un peu de chance, ce sera la dernière modification de cette réponse :)

J'ai copié ce code dans un référentiel GitHub pour faciliter l'obtention du code (au lieu de copier / coller, vous pouvez simplement télécharger la source ), et il est plus facile pour quiconque de suggérer une modification en soumettant un tirage -request de GitHub.

La source est disponible sous Apache License 2.0. Patchs * bienvenus!


  • "patch" est ce que les personnes âgées comme moi appellent une "pull request".

2
Tout simplement génial! Merci! Et pour ceux qui ne le savent pas: vous pouvez compléter le code ci-dessus dans le terminal: gcc -o dircnt dircnt.cet l'utilisation est comme ça./dircnt some_dir
aesede

Existe-t-il un moyen simple de rendre cela récursif?
ck_

@ck_ Bien sûr, cela peut facilement être rendu récursif. Avez-vous besoin d'aide pour trouver une solution ou voulez-vous que j'écrive le tout?
Christopher Schultz

1
@ChristopherSchultz, les benchmarks que vous avez publiés ci-dessus - quelle était la taille du répertoire en question?
Dom Vinyard

1
Je voulais vraiment l'utiliser en Python, alors je l'ai empaqueté en tant que package ffcount . Merci d'avoir rendu le code disponible @ChristopherSchultz!
GjjvdBurg

35

Avez-vous essayé de trouver? Par exemple:

find . -name "*.ext" | wc -l

1
Cela trouvera récursivement les fichiers dans le répertoire courant.
mark4o

Sur mon système, find /usr/share | wc -l(~ 137000 fichiers) est environ 25% plus rapide que ls -R /usr/share | wc -l(~ 160000 lignes, y compris les noms de répertoires , les totaux de répertoires et les lignes vides) lors de la première exécution de chaque et au moins deux fois plus rapide lors de la comparaison des exécutions suivantes (mises en cache).
Suspendu jusqu'à nouvel ordre.

11
S'il ne veut que le répertoire courant, pas l'arborescence entière de manière récursive, il peut ajouter l'option -maxdepth 1 pour trouver.
igustin le

3
Il semble que la raison findsoit plus rapide que la lsfaçon dont vous l'utilisez ls. Si vous arrêtez le tri lset que vous findavez des performances similaires.
Christopher Schultz

17

find, ls et perl testés sur 40000 fichiers: même vitesse (même si je n'ai pas essayé de vider le 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

et avec perl opendir / readdir, en même temps:

[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

note: j'ai utilisé / bin / ls -f pour m'assurer de contourner l'option alias qui pourrait ralentir un peu et -f pour éviter l'ordre des fichiers. ls sans -f est deux fois plus lent que find / perl sauf si ls est utilisé avec -f, cela semble être le même temps:

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

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

Je voudrais également avoir un script pour demander directement au système de fichiers sans toutes les informations inutiles.

tests basés sur la réponse de Peter van der Heijden, glenn jackman et mark4o.

Thomas


5
Vous devez absolument vider le cache entre les tests. La première fois que j'exécute ls -l | wc -lsur un dossier sur un disque dur externe de 2,5 "avec 1M de fichiers, l'opération prend environ 3 minutes. La deuxième fois, cela prend 12 secondes IIRC. Cela peut également dépendre de votre système de fichiers. I utilisait Btrfs.
Behrang Saeedzadeh

Merci, l'extrait de code perl est une solution pour moi. $ 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

5

Vous pouvez modifier la sortie en fonction de vos besoins, mais voici un bash one-liner que j'ai écrit pour compter et rapporter de manière récursive le nombre de fichiers dans une série de répertoires nommés numériquement.

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

Cela recherche récursivement tous les fichiers (pas les répertoires) dans le répertoire donné et renvoie les résultats dans un format de type hachage. De simples ajustements à la commande find pourraient rendre le type de fichiers que vous cherchez à compter plus spécifique, etc.

Résultats en quelque chose comme ceci:

1 => 38,
65 => 95052,
66 => 12823,
67 => 10572,
69 => 67275,
70 => 8105,
71 => 42052,
72 => 1184,

1
J'ai trouvé l'exemple un peu déroutant. Je me demandais pourquoi il y avait des nombres sur la gauche, au lieu des noms de répertoire. Merci pour cela, j'ai fini par l'utiliser avec quelques modifications mineures. (comptage des répertoires et suppression du nom du dossier de base. for i in $ (ls -1. | sort -n); {echo "$ i => $ (find $ {i} | wc -l)";}
TheJacobTaylor

Les numéros sur la gauche sont les noms de mon répertoire à partir de mes données d'exemple. Désolé, c'était déroutant.
mightybs

1
ls -1 ${dir}ne fonctionnera pas correctement sans plus d'espaces. De plus, il n'y a aucune garantie que le nom renvoyé par lspuisse être transmis à find, car lséchappe les caractères non imprimables pour la consommation humaine. ( mkdir $'oddly\nnamed\ndirectory'si vous voulez un cas de test particulièrement intéressant). Voir pourquoi vous ne devriez pas analyser la sortie de ls (1)
Charles Duffy

4

Étonnamment pour moi, une découverte simple est très comparable à ls -f

> time ls -f my_dir | wc -l
17626

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

contre

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

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

Bien sûr, les valeurs de la troisième décimale décalent un peu chaque fois que vous exécutez l'une de ces dernières, elles sont donc fondamentalement identiques. Notez cependant que cela findrenvoie une unité supplémentaire, car il compte le répertoire lui-même (et, comme mentionné précédemment, ls -fretourne deux unités supplémentaires, car il compte également. Et ..).


4

Juste en ajoutant ceci par souci d'exhaustivité. La bonne réponse a bien sûr déjà été publiée par quelqu'un d'autre, mais vous pouvez également obtenir un nombre de fichiers et de répertoires avec le programme d'arborescence.

Exécutez la commande tree | tail -n 1pour obtenir la dernière ligne, qui dira quelque chose comme "763 répertoires, 9290 fichiers". Cela compte les fichiers et les dossiers de manière récursive, à l'exclusion des fichiers cachés, qui peuvent être ajoutés avec l'indicateur -a. Pour référence, il a fallu 4,8 secondes sur mon ordinateur pour que tree compte tout mon répertoire personnel, qui était de 24777 répertoires, 238680 fichiers. find -type f | wc -la pris 5,3 secondes, une demi-seconde de plus, donc je pense que tree est assez compétitif en termes de vitesse.

Tant que vous n'avez pas de sous-dossiers, l'arborescence est un moyen rapide et facile de compter les fichiers.

De plus, et uniquement pour le plaisir, vous pouvez utiliser tree | grep '^├'pour afficher uniquement les fichiers / dossiers dans le répertoire actuel - il s'agit essentiellement d'une version beaucoup plus lente de ls.


Brew install tailpour OS X.
The Unfun Cat

@TheUnfunCat taildevrait déjà être installé sur votre système Mac OS X.
Christopher Schultz

4

Nombre de fichiers Linux rapide

Le nombre de fichiers Linux le plus rapide que je connaisse est

locate -c -r '/home'

Il n'est pas nécessaire d'invoquer grep! Mais comme mentionné, vous devriez avoir une nouvelle base de données (mise à jour quotidiennement par un travail cron, ou manuelle parsudo updatedb ).

De l' homme localiser

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

De plus, sachez qu'il compte également les répertoires sous forme de fichiers!


BTW: Si vous voulez un aperçu de vos fichiers et répertoires sur votre type de système

locate -S

Il affiche le nombre de répertoires, de fichiers, etc.


notez que vous devez vous assurer que la base de données est à jour
phuclv

1
LOL si vous avez déjà tous les décomptes dans une base de données, vous pouvez certainement compter rapidement. :)
Christopher Schultz

3

Ecrire ceci ici car je n'ai pas assez de points de réputation pour commenter une réponse, mais je suis autorisé à laisser le mien réponse, ce qui n'a pas de sens. En tous cas...

En ce qui concerne la réponse de Christopher Schultz , je suggère de changer stat en lstat et éventuellement d'ajouter une vérification des limites pour éviter un débordement de tampon:

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;
}

La suggestion d'utiliser lstat est d'éviter de suivre des liens symboliques qui pourraient conduire à des cycles si un répertoire contient un lien symbolique vers un répertoire parent.


2
Modifier parce que l'utilisation de lstatétait une bonne suggestion et vous méritez du karma pour cela. Cette suggestion a été incorporée dans mon code posté ci-dessus et, maintenant, sur GitHub.
Christopher Schultz

2

Vous pouvez essayer si l'utilisation de opendir()et readdir()dans Perlest plus rapide. Pour un exemple de ces fonctions, regardez ici


2
utilisation: perl -e 'opendir D, "."; @files = readdir D; Closedir D; print scalar (@files) '
glenn jackman

2

Cette réponse ici est plus rapide que presque tout le reste de cette page pour les très grands répertoires très imbriqués:

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

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


1
Agréable. Étant donné que vous avez déjà une base de données à jour de tous les fichiers, inutile de recommencer. Mais malheureusement, vous devez vous assurer que la commande updatedb est déjà exécutée et terminée pour cette méthode.
Chris Reid

vous n'avez pas besoin de grep. Utiliser locate -c -r '/path'comme dans la solution d'
abu_bua

2

Je suis venu ici en essayant de compter les fichiers dans un ensemble de données de ~ 10K dossiers avec ~ 10K fichiers chacun. Le problème avec de nombreuses approches est qu'elles statuent implicitement 100 millions de fichiers, ce qui prend des années.

J'ai pris la liberté d'étendre l'approche par christopher-schultz afin qu'elle supporte le passage de répertoires via args (son approche récursive utilise également stat).

Mettez ce qui suit dans le fichier 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;
}

Après un, gcc -o dircnt_args dircnt_args.cvous pouvez l'invoquer comme ceci:

dircnt_args /your/dirs/*

Sur 100M de fichiers dans 10K dossiers, ce qui précède se termine assez rapidement (~ 5 min pour la première exécution, suivi sur le cache: ~ 23 s).

La seule autre approche qui a terminé en moins d'une heure était ls avec environ 1 min sur le cache: ls -f /your/dirs/* | wc -l. Le décompte est cependant décalé de quelques nouvelles lignes par répertoire ...

Autre que prévu, aucune de mes tentatives n'est findrevenue dans l'heure: - /


Pour quelqu'un qui n'est pas un programmeur C, pouvez-vous expliquer pourquoi ce serait plus rapide, et comment il est capable d'obtenir la même réponse sans faire la même chose?
mlissner

vous n'avez pas besoin d'être un programmeur C, comprenez simplement ce que signifie statuer un fichier et comment les répertoires sont représentés: les répertoires sont essentiellement des listes de noms de fichiers et d'inodes. Si vous stat un fichier, vous accédez à l'inode qui se trouve quelque part sur le lecteur pour obtenir par exemple des informations telles que la taille du fichier, les autorisations, .... Si vous êtes simplement intéressé par les décomptes par répertoire, vous n'avez pas besoin d'accéder aux informations d'inode, ce qui peut vous faire gagner beaucoup de temps.
Jörn Hees

Ce segfaults sur Oracle linux, gcc version 4.8.5 20150623 (Red Hat 4.8.5-28.0.1) (GCC) ... les chemins relatifs et les fs distants semblent en être la cause
Rondo

2

Le moyen le plus rapide sur linux (la question est étiquetée comme linux), est d'utiliser l'appel système direct. Voici un petit programme qui compte les fichiers (uniquement, pas de répertoires) dans un répertoire. Vous pouvez compter des millions de fichiers et c'est environ 2,5 fois plus rapide que "ls -f" et environ 1,3 à 1,5 fois plus rapide que la réponse 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: Ce n'est pas récursif mais vous pouvez le modifier pour y parvenir.


1
Je ne suis pas sûr d'être d'accord pour dire que c'est plus rapide. Je n'ai pas retracé tout ce que le compilateur fait avec opendir/ readdir, mais je soupçonne que cela se résume à presque le même code à la fin. Faire des appels système de cette façon n'est pas non plus portable et, comme l'ABI Linux n'est pas stable, un programme compilé sur un système n'est pas garanti de fonctionner correctement sur un autre (bien qu'il soit assez bon de compiler quoi que ce soit à partir des sources sur n'importe quel système * NIX IMO ). Si la vitesse est la clé, c'est une bonne solution si elle améliore réellement la vitesse - je n'ai pas comparé les programmes séparément.
Christopher Schultz

1

lspasse plus de temps à trier les noms de fichiers, l'utilisation de -fpour désactiver le tri permettra d'économiser un certain temps :

ls -f | wc -l

ou vous pouvez utiliser find:

find . -type f | wc -l

0

Je me suis rendu compte que ne pas utiliser dans le traitement de la mémoire lorsque vous avez une énorme quantité de données est plus rapide que de "piping" les commandes. J'ai donc enregistré le résultat dans un fichier et après l'avoir analysé

ls -1 /path/to/dir > count.txt && cat count.txt | wc -l

ce n'est pas la solution la plus rapide car les disques durs sont extrêmement lents. Il existe d'autres moyens plus efficaces qui ont été publiés des années avant vous
phuclv

0

Vous devriez utiliser "getdents" à la place de ls / find

Voici un très bon article qui décrit l'approche getdents.

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

Voici l'extrait:

ls et pratiquement toutes les autres méthodes de listage d'un répertoire (y compris python os.listdir, find.) reposent sur la libc readdir (). Cependant readdir () ne lit que 32K d'entrées de répertoire à la fois, ce qui signifie que si vous avez beaucoup de fichiers dans le même répertoire (c'est-à-dire 500M d'entrées de répertoire), il va falloir un temps incroyablement long pour lire toutes les entrées de répertoire , en particulier sur un disque lent. Pour les répertoires contenant un grand nombre de fichiers, vous devrez creuser plus profondément que les outils qui reposent sur readdir (). Vous devrez utiliser directement l'appel système getdents (), plutôt que les méthodes d'assistance de la libc.

Nous pouvons trouver le code C pour lister les fichiers en utilisant getdents () à partir d' ici :

Il y a deux modifications que vous devrez faire pour lister rapidement tous les fichiers d'un répertoire.

Tout d'abord, augmentez la taille de la mémoire tampon de X à quelque chose comme 5 mégaoctets.

#define BUF_SIZE 1024*1024*5

Puis modifiez la boucle principale où il imprime les informations sur chaque fichier dans le répertoire pour sauter les entrées avec inode == 0. Je l'ai fait en ajoutant

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

Dans mon cas, je ne me souciais vraiment que des noms de fichiers dans le répertoire, donc j'ai également réécrit l'instruction printf () pour n'imprimer que le nom de fichier.

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

Compilez-le (il n'a pas besoin de bibliothèques externes, donc c'est super simple à faire)

gcc listdir.c -o listdir

Maintenant, cours

./listdir [directory with insane number of files]

Notez que Linux fait une lecture anticipée, donc readdir()n'est pas vraiment lent. J'ai besoin d'un chiffre solide avant de croire qu'il vaut la peine de jeter la portabilité pour ce gain de performances.
fuz

-1

Je préfère la commande suivante pour suivre les changements dans le nombre de fichiers dans un répertoire.

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

La commande garde une fenêtre ouverte pour garder une trace du nombre de fichiers qui se trouvent dans le répertoire avec un taux de rafraîchissement de 0,1 sec.


êtes-vous sûr que cela ls | wc -lse terminera pour un dossier avec des milliers ou des millions de fichiers en 0,01s? même la vôtre lsest extrêmement inefficace par rapport à d'autres solutions. Et l'OP veut juste obtenir le décompte, pas assis là à regarder le changement de sortie
phuclv

Bien. Bien. J'ai trouvé une solution élégante qui fonctionne pour moi. Je voudrais partager la même chose, donc fait. Je ne sais pas que la commande «ls» sous Linux est très inefficace. Qu'est-ce que vous utilisez à la place de cela? Et 0,01 s est le taux de rafraîchissement. Pas le temps. si vous n'avez pas utilisé watch, veuillez consulter les pages de manuel.
Anoop Toffy

watchEh bien, j'ai lu le manuel après ce commentaire et j'ai vu que 0,01 s (et non 0,1 s) est un nombre irréaliste car le taux de rafraîchissement de la plupart des écrans de PC n'est que de 60 Hz, et cela ne répond en aucun cas à la question. L'OP a posé des questions sur "Fast Linux File Count for a large number of files". Vous n'avez pas non plus lu les réponses disponibles avant de publier
phuclv

J'ai lu les réponses. Mais ce que j'ai posté est un moyen de suivre l'évolution du nombre de fichiers dans un répertoire. par exemple: lors de la copie d'un fichier d'un emplacement à un autre, le nombre de fichiers conserve les modifications. avec la méthode que j'ai affichée, on peut en garder une trace. J'accepte que le message que j'ai publié ne modifie ou améliore aucun des messages précédents.
Anoop Toffy

-2

Les 10 premiers directeurs avec le plus grand nombre de fichiers.

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

3
Cela ressemble certainement étonnamment à la réponse (avec les mêmes bogues) écrite par mightybs . Si vous comptez étendre ou modifier le code écrit par quelqu'un d'autre, il est approprié de le créditer. Comprendre suffisamment le code que vous utilisez dans vos réponses pour identifier et corriger ses bogues est encore plus approprié.
Charles Duffy
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.