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 -l
prend 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?
Réponses:
Par défaut,
ls
trie 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 -f
option pour désactiver le tri.Notez que cela permettra aussi
-a
, donc.
,..
et d' autres fichiers commençant par.
seront comptés.la source
ls
.stat()
appells
fait sur chaque fichier.find
ne fonctionnestat()
donc pas plus vite.ls -f
nonstat()
plus. Mais bien sûr les deuxls
etfind
callstat()
lorsque certaines options sont utilisées, telles quels -l
oufind -mtime
.ls -fR | wc -l
Le moyen le plus rapide est un programme spécialement conçu, comme celui-ci:
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):
Ce dernier,,
dircnt
est 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
count
fonction elle-même ne signale pas vraiment les erreurs. Les seuls appels qui peuvent vraiment échouer sontopendir
etstat
(si vous n'avez pas de chance et que votre systèmedirent
contient 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 quePATH_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.MODIFIER 2017-01-17
J'ai incorporé deux changements suggérés par @FlyingCodeMonkey:
lstat
place destat
. 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é.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!
la source
gcc -o dircnt dircnt.c
et l'utilisation est comme ça./dircnt some_dir
Avez-vous essayé de trouver? Par exemple:
la source
find /usr/share | wc -l
(~ 137000 fichiers) est environ 25% plus rapide quels -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).find
soit plus rapide que lals
façon dont vous l'utilisezls
. Si vous arrêtez le trils
et que vousfind
avez des performances similaires.find, ls et perl testés sur 40000 fichiers: même vitesse (même si je n'ai pas essayé de vider le cache):
et avec perl opendir / readdir, en même temps:
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:
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
la source
ls -l | wc -l
sur 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 utilisaitBtrfs
.$ time perl -e 'opendir D, "."; @files = readdir D; closedir D; print scalar(@files)."\n"' 1315029 real 0m0.580s user 0m0.302s sys 0m0.275s
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.
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:
la source
ls -1 ${dir}
ne fonctionnera pas correctement sans plus d'espaces. De plus, il n'y a aucune garantie que le nom renvoyé parls
puisse être transmis àfind
, carls
é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)Étonnamment pour moi, une découverte simple est très comparable à ls -f
contre
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
find
renvoie une unité supplémentaire, car il compte le répertoire lui-même (et, comme mentionné précédemment,ls -f
retourne deux unités supplémentaires, car il compte également. Et ..).la source
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 1
pour 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 -l
a 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 dels
.la source
Brew install tail
pour OS X.tail
devrait déjà être installé sur votre système Mac OS X.Nombre de fichiers Linux rapide
Le nombre de fichiers Linux le plus rapide que je connaisse est
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 par
sudo updatedb
).De l' homme localiser
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
Il affiche le nombre de répertoires, de fichiers, etc.
la source
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:
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.
la source
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.Vous pouvez essayer si l'utilisation de
opendir()
etreaddir()
dansPerl
est plus rapide. Pour un exemple de ces fonctions, regardez icila source
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"
la source
locate -c -r '/path'
comme dans la solution d'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
:Après un,
gcc -o dircnt_args dircnt_args.c
vous pouvez l'invoquer comme ceci: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
find
revenue dans l'heure: - /la source
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.
PS: Ce n'est pas récursif mais vous pouvez le modifier pour y parvenir.
la source
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.ls
passe plus de temps à trier les noms de fichiers, l'utilisation de-f
pour désactiver le tri permettra d'économiser un certain temps :ou vous pouvez utiliser
find
:la source
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é
la source
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.
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
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.
Compilez-le (il n'a pas besoin de bibliothèques externes, donc c'est super simple à faire)
Maintenant, cours
la source
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.Je préfère la commande suivante pour suivre les changements dans le nombre de fichiers dans un répertoire.
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.
la source
ls | wc -l
se terminera pour un dossier avec des milliers ou des millions de fichiers en 0,01s? même la vôtrels
est extrêmement inefficace par rapport à d'autres solutions. Et l'OP veut juste obtenir le décompte, pas assis là à regarder le changement de sortiewatch
Eh 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 publierLes 10 premiers directeurs avec le plus grand nombre de fichiers.
la source