CentOS 5.9
Je suis tombé sur un problème l’autre jour où un répertoire contenait beaucoup de fichiers. Pour le compter, j'ai couruls -l /foo/foo2/ | wc -l
Il s'avère qu'il y avait plus d'un million de fichiers dans un seul répertoire (histoire longue - la cause principale est corrigée).
Ma question est la suivante: existe-t-il un moyen plus rapide de compter? Quel serait le moyen le plus efficace d’obtenir le décompte?
ls -l|wc -l
serait un par un en raison du nombre total de blocs dans la première ligne dels -l
sortie-A
drapeau.-l
est également problématique en raison de la lecture des métadonnées du fichier afin de générer le format de liste étendue. Forcer NOT-l
en utilisant\ls
est une bien meilleure option (cela-1
est supposé lors de la sortie de la tuyauterie.) Voir la réponse de Gilles pour la meilleure solution ici.ls -l
ne génère aucun fichier caché ni les entrées.
et..
.ls -a
la sortie inclut les fichiers cachés, y compris.
et..
tandis que lals -A
sortie inclut les fichiers cachés, à l' exclusion de.
et..
. Dans la réponse de Gilles, l'dotglob
option shell bash fait en sorte que l'extension inclue les fichiers cachés, à l' exclusion de.
et..
.Réponses:
Réponse courte:
(Ceci inclut
.
et..
, donc soustrayez 2.)Lorsque vous répertoriez les fichiers dans un répertoire, trois problèmes courants peuvent survenir:
ls
commande le font.stat
pour récupérer des métadonnées sur chaque entrée de répertoire, par exemple s'il s'agit d'un répertoire.N ° 3 est de loin le plus cher, car il nécessite le chargement d’un inode pour chaque fichier. En comparaison, tous les noms de fichiers nécessaires pour # 1 sont stockés de manière compacte dans quelques blocs. # 2 gaspille un peu de temps CPU mais ce n'est souvent pas un facteur décisif.
S'il n'y a pas de nouvelles lignes dans les noms de fichiers, un simple
ls -A | wc -l
indique le nombre de fichiers contenus dans le répertoire. Attention, si vous avez un alias pourls
, cela peut déclencher un appel àstat
(par exemple,ls --color
ou si vous avezls -F
besoin de connaître le type de fichier, ce qui nécessite un appel àstat
), c'est-à-dire à partir de la ligne de commande, appelezcommand ls -A | wc -l
ou\ls -A | wc -l
évitez un alias.S'il y a des sauts de lignes dans le nom du fichier, le fait de savoir si les sauts de lignes sont répertoriés dépend de la variante Unix. GNU coreutils et BusyBox s’affichent
?
par défaut pour une nouvelle ligne, donc ils sont sûrs.Appelez
ls -f
pour lister les entrées sans les trier (# 2). Cela s'allume automatiquement-a
(au moins sur les systèmes modernes). L'-f
option est dans POSIX mais avec un statut optionnel; la plupart des implémentations le supportent, mais pas BusyBox. L'option-q
remplace les caractères non imprimables, y compris les retours à la ligne, par?
; il s'agit de POSIX mais n'est pas pris en charge par BusyBox, alors omettez-le si vous avez besoin de la prise en charge de BusyBox au détriment du nombre excessif de fichiers dont le nom contient un caractère de nouvelle ligne.Si le répertoire ne comporte pas de sous-répertoires, la plupart des versions de
find
ne feront pas appelstat
à ses entrées (optimisation du répertoire feuille: un répertoire avec un nombre de liens égal à 2 ne peut pas avoir de sous-répertoires, ilfind
n'est donc pas nécessaire de rechercher les métadonnées des entrées sauf condition telle que l'-type
exige). Ilfind . | wc -l
existe donc un moyen rapide et portable de compter les fichiers dans un répertoire, à condition que celui-ci ne comporte aucun sous-répertoire et qu'aucun nom de fichier ne contienne de nouvelle ligne.Si le répertoire ne contient pas de sous-répertoires mais que les noms de fichiers peuvent contenir des nouvelles lignes, essayez l'une d'entre elles (la deuxième devrait être plus rapide si elle est prise en charge, mais peut ne pas l'être de manière notable).
D'autre part, n'utilisez pas
find
si le répertoire a des sous-répertoires: même desfind . -maxdepth 1
appelsstat
à chaque entrée (au moins avec GNU find et BusyBox find). Vous évitez de trier (# 2) mais vous payez le prix d'une recherche d'inode (# 3) qui tue les performances.Dans le shell sans outils externes, vous pouvez exécuter compter les fichiers du répertoire en cours avec
set -- *; echo $#
. Cela manque des fichiers point (fichiers dont le nom commence par.
) et signale 1 au lieu de 0 dans un répertoire vide. C’est le moyen le plus rapide de compter les fichiers dans les petits répertoires car il n’a pas besoin de démarrer un programme externe, mais (sauf dans zsh) une perte de temps pour les plus grands répertoires en raison de l’étape de tri (# 2).En bash, c’est un moyen fiable de compter les fichiers du répertoire en cours:
Dans ksh93, il s'agit d'un moyen fiable de compter les fichiers du répertoire en cours:
En zsh, c'est un moyen fiable de compter les fichiers du répertoire en cours:
Si vous avez l'
mark_dirs
ensemble des options, assurez - vous de le désactiver:a=(*(DNoN^M))
.Dans n’importe quel shell POSIX, c’est un moyen fiable de compter les fichiers du répertoire en cours:
Toutes ces méthodes trient les noms de fichiers, à l’exception de celui de zsh.
la source
find -maxdepth 1
suit facilement\ls -U
tant que vous n'ajoutez rien qui ressemble à une-type
déclaration qui doit faire des vérifications supplémentaires. Êtes-vous sûr que GNU trouve réellement des appelsstat
? Même le ralentissementfind -type
n'est rien comparé à combien dels -l
marges si vous faites retourner les détails du fichier. D'autre part, le vainqueur de la vitesse clairezsh
utilise le glob sans tri. (les globs triés sont 2x plus lents que ceux quils
ne trient pas sont 2x plus rapides). Je me demande si les types de système de fichiers auraient un effet significatif sur ces résultats.strace
. Ceci n'est vrai que si le répertoire a des sous-répertoires: sinonfind
l'optimisation des répertoires feuille entre en jeu (même sans-maxdepth 1
), j'aurais dû le mentionner. Beaucoup de choses peuvent affecter le résultat, y compris le type de système de fichiers (l'appelstat
coûte beaucoup plus cher sur les systèmes de fichiers qui représentent des répertoires sous forme de listes linéaires que sur les systèmes de fichiers qui représentent des répertoires sous forme d'arbres), si les inodes ont tous été créés ensemble et sont donc proches. sur le disque, cache froide ou chaude, etc.ls -f
le moyen le plus fiable d’empêcher les appelsstat
; c’est souvent simplement décrit aujourd’hui comme "la sortie n’est pas triée" (ce qu’elle provoque également), et inclut.
et..
.-A
et-U
ne sont pas des options standard.\ls -afq *[0-9].pdb | wc -l
version sh (AT&T Research) 93u+ 2012-08-01
sur mon système basé sur Debian,FIGNORE
ne semble pas fonctionner. Les entrées.
et..
sont incluses dans le tableau résultantEst considérablement plus rapide sur ma machine mais le
.
répertoire local est ajouté au compte.la source
-type
paramètrefind
doit être plus rapide quels
-mindepth 1
pour omettre le répertoire lui-même.ls -1U
avant que le canal ne dépense un peu moins de ressources, car il ne tente pas de trier les entrées de fichiers, il les lit simplement au fur et à mesure de leur tri dans le dossier du disque. Il produit également moins de sortie, ce qui signifie légèrement moins de travail pourwc
.Vous pouvez également utiliser
ls -f
ce qui est plus ou moins un raccourci pourls -1aU
.Je ne sais pas s'il existe un moyen économe en ressources de le faire via une commande sans canalisation.
la source
Un autre point de comparaison. Bien que n'étant pas un shell de base, ce programme C ne fait rien de trop. Notez que les fichiers cachés sont ignorés pour correspondre à la sortie de
ls|wc -l
(ls -l|wc -l
est désactivé par un en raison du nombre total de blocs dans la première ligne de sortie).la source
readdir()
API stdio ajoute une surcharge et ne vous permet pas de contrôler la taille de la mémoire tampon transmise à l'appel système sous-jacent (getdents
sous Linux)Tu pourrais essayer
perl -e 'opendir($dh,".");$i=0;while(readdir $dh){$i++};print "$i\n";'
Il serait intéressant de comparer les timings avec votre pipe shell.
la source
find -maxdepth 1 | wc -l
,\ls -AU | wc -l
et lezsh
nombre en fonction glob et tableau non tri). En d’autres termes, il défie les options avec diverses inefficacités telles que le tri ou la lecture de propriétés de fichiers superflues. Je me risquerais à dire que cela ne vous rapporte rien non plus, cela ne vaut pas la peine d'utiliser une solution plus simple à moins que vous ne soyez déjà en Perl :).
et..
dans le compte. Vous devez donc soustraire deux pour obtenir le nombre réel de fichiers (et de sous-répertoires). En Perl moderne,perl -E 'opendir $dh, "."; $i++ while readdir $dh; say $i - 2'
le ferais.À partir de cette réponse , je peux penser à celle-ci comme une solution possible.
Copiez le programme C ci-dessus dans le répertoire dans lequel les fichiers doivent être listés. Puis exécutez ces commandes:
la source
ls -f
, ne pas filtrer dud_type
tout, juste surd->d_ino != 0
; 3) soustrayez 2 pour.
et..
.ls -f
.Une solution réservée à Bash, ne nécessitant aucun programme externe, mais ne sachant pas combien elle est efficace:
la source
Le moyen le plus efficace en termes de ressources ne ferait probablement pas appel à des processus externes. Donc, je parie sur ...
la source
Après avoir résolu le problème de la réponse de @Joel, où il a été ajouté en
.
tant que fichier:find /foo/foo2 -maxdepth 1 | tail -n +2 | wc -l
tail
supprime simplement la première ligne, ce qui signifie que ce.
n'est plus compté.la source
wc
entrée n'est pas très efficace car le temps système augmente de manière linéaire en fonction de la taille de l'entrée. Dans ce cas, pourquoi ne pas simplement décrémenter le décompte final pour le compenser d'une unité, ce qui correspond à une opération à temps constant:echo $(( $(find /foo/foo2 -maxdepth 1 | wc -l) - 1))
let count = $(find /foo/foo2 -maxdepth 1 | wc -l) - 2
os.listdir () en python peut faire le travail pour vous. Il donne un tableau du contenu du répertoire, en excluant le spécial '.' et '..' fichiers. En outre, inutile de s’inquiéter des fichiers contenant des caractères spéciaux tels que "\ n" dans le nom.
Ce qui suit est le temps pris par la commande python ci-dessus par rapport à la commande 'ls -Af'.
la source
ls -1 | wc -l
vient immédiatement à mon esprit. Que cels -1U
soit plus rapide quels -1
purement théorique - la différence devrait être négligeable, mais pour les très grands annuaires.la source
Pour exclure les sous-répertoires du nombre, voici une variation de la réponse acceptée de Gilles:
Le
$(( ))
développement arithmétique externe soustrait la sortie du deuxième$( )
sous-shell du premier$( )
. Le premier$( )
est exactement celui de Gilles d'en haut. La deuxième$( )
sortie indique le nombre de répertoires "liés" à la cible. Cela vient dels -od
(substituezls -ld
si vous le souhaitez), où la colonne qui répertorie le nombre de liens durs a cela comme signification spéciale pour les répertoires. Le nombre de « lien » comprend.
,..
et les sous - répertoires.Je n'ai pas testé les performances, mais cela semble être similaire. Il ajoute une statistique du répertoire cible et une surcharge pour le sous-shell et le canal ajoutés.
la source
Je pense que echo * serait plus efficace que n'importe quelle commande 'ls':
la source
echo 'Hello World'|wc -w
produit2
.