Y a-t-il quelque chose de plus rapide que `trouver. | wc -l` compter les fichiers dans un répertoire?

8

Il n'est pas rare que je doive compter le nombre de fichiers dans un répertoire, parfois cela se compte en millions.

Existe-t-il un meilleur moyen que de simplement les énumérer et de les compter find . | wc -l? Y a-t-il une sorte d'appel de système de fichiers que vous pouvez faire sur ext3 / 4 qui nécessite moins d'E / S?

MattPark
la source
3
Vous comptez non seulement les fichiers, mais aussi les répertoires. Si vous voulez seulement compter les fichiers, utilisez "find. -Type f | wc -l" si vous voulez compter les liens symboliques et les fichiers normaux, utilisez "find. -Type f -or -type l | wc -l"
FSMaxB
Un répertoire est une sorte de fichier, tout comme les périphériques, les liens symboliques et les sockets. Les fichiers normaux sont un sous-ensemble de fichiers.
Toby Speight
1
L'exemple que vous donnez suggère que vous voulez un décompte récursif - sinon, vous en avez besoin find -maxdepth 1. Notez qu'avec votre approche actuelle, vous compterez deux fois n'importe quel nom contenant un caractère de nouvelle ligne.
Toby Speight

Réponses:

13

Pas une accélération fondamentale mais au moins quelque chose :)

find . -printf \\n | wc -l

Vous n'avez vraiment pas besoin de passer la liste des noms de fichiers, juste les sauts de ligne suffisent. Cette variante est environ 15% plus rapide sur mon Ubuntu 12.04.3 lorsque les répertoires sont mis en cache dans la RAM. De plus, cette variante fonctionnera correctement avec des noms de fichiers contenant des retours à la ligne.

Fait intéressant, cette variante semble être un peu plus lente que celle ci-dessus:

find . -printf x | wc -c

Cas particulier - mais vraiment rapide

Si le répertoire est sur son propre système de fichiers, vous pouvez simplement compter les inodes:

df -i .

Si le nombre de répertoires et de fichiers dans d'autres répertoires que celui compté ne change pas grand-chose, vous pouvez simplement soustraire ce nombre connu du df -irésultat actuel . De cette façon, vous pourrez compter les fichiers et répertoires très rapidement.

pabouk
la source
"Cette variante est environ 15% plus rapide ...", me demande-t-il s'il existe une sorte de truc pratique que vous utilisez pour chronométrer ces derniers?
Brian Z
4
@BrianZ: Vous pouvez chronométrer une commande en ajoutant la commande au temps. time find /usr/src/ -printf \\n | wc -l, vous pouvez vider les caches entre les runs avecsudo sync && sudo sysctl -w vm.drop_caches=3
MattPark
J'ai donc vu une augmentation constante de 2% de la vitesse avec l'une des 2 premières options sans mise en cache. Alors oui, c'est une façon plutôt cool de le faire. Compter les inodes est certainement le meilleur si votre environnement est configuré pour cela. Je n'y avais pas pensé.
MattPark
Est -printf xcensé être le même que -printf '\0'? Je ne le vois pas mentionné dans les documents.
CMCDragonkai
@CMCDragonkai: L'action -printffonctionne de manière similaire à la printf()fonction en C avec la principale différence que les %directives ont une signification différente. L'action est invoquée pour chaque fichier trouvé. Cela signifie que -printf xva imprimer le caractère xpour chaque fichier trouvé (essayez-le!) Et -printf '\0'imprimera le caractère NULL (code ASCII 0) pour chaque fichier trouvé. -printf '\0'n'a pas de signification particulière. Les deux fonctionneront de la même manière dans l'exemple avec wc -cdans cette réponse.
pabouk
3

J'ai écrit ffcnt exactement dans ce but. Il récupère l'offset physique des répertoires eux-mêmes avec l' fiemapioctl, puis planifie la traversée du répertoire en plusieurs passes séquentielles pour réduire l'accès aléatoire. Que vous obteniez réellement une accélération par rapport à find | wc dépend de plusieurs facteurs:

  • type de système de fichiers: les systèmes de fichiers tels que ext4 qui prennent en charge l' fiemapioctl bénéficieront le plus
  • vitesse d'accès aléatoire: les disques durs bénéficient bien plus que les SSD
  • Disposition des répertoires: plus le nombre de répertoires imbriqués est élevé, plus le potentiel d'optimisation est élevé

le (re) montage avec relatimeou même nodiratimepeut également améliorer la vitesse (pour toutes les méthodes) lorsque les accès entraîneraient autrement des mises à jour des métadonnées.

the8472
la source
Cette dernière phrase est un bon conseil! Je pense que le lien vers votre programme serait amélioré si vous ajoutiez un résumé de son fonctionnement. Nous préférons des réponses complètes en elles-mêmes, au cas où quelque chose de mauvais arriverait à la ressource liée (mais gardez le lien aussi, bien sûr).
Toby Speight
2

En fait, sur mon système (Arch Linux) cette commande

   ls -A | wc -l

est plus rapide que tout ce qui précède:

   $ time find . | wc -l
  1893

   real    0m0.027s
   user    0m0.004s
   sys     0m0.004s
   $ time find . -printf \\n  | wc -l
   1893

   real    0m0.009s
   user    0m0.000s
   sys     0m0.008s
   $ time find . -printf x  | wc -c
   1893

   real    0m0.009s
   user    0m0.000s
   sys     0m0.008s
   $ time ls -A | wc -l
   1892

   real    0m0.007s
   user    0m0.000s
   sys     0m0.004s
MariusMatutiae
la source
Je pense que le problème avec ls est qu'il retourne souvent quelque chose comme /bin/ls: Argument list too longsi vous utilisez la globalisation, mais là encore il peut fonctionner de manière récursive comme find aussi, donc c'est peut-être quelque chose à considérer, n'utilisez pas find si ce n'est pas nécessaire.
MattPark
Il semble si tard (plusieurs années) de commenter cela, mais ls -Alister uniquement les fichiers dans le répertoire courant alors que findsans -maxdepth 1argument fera une recherche récursive dans tous les sous-répertoires.
Luciano