Pourquoi la commande «find | grep 'filename' ”tellement plus lent que" find 'filename' "?

10

J'ai essayé les deux commandes et la commande find | grep 'filename' est beaucoup plus lente que la find 'filename' commande simple .

Quelle serait une explication appropriée de ce comportement?

yoyo_fun
la source
2
Vous répertoriez chaque fichier avec find, puis passez les données à grep pour les traiter. Avec find utilisé seul, il vous manque l'étape de passer chaque fichier répertorié à grep pour analyser la sortie. Ce sera donc plus rapide.
Raman Sailopal du
Plus lent dans quel sens? Les commandes prennent-elles un temps différent pour s'exécuter?
Kusalananda
1
Je ne peux pas reproduire cela localement. Si quoi que ce soit, time find "$HOME" -name '.profile'rapporte un temps plus long que time find "$HOME" | grep -F '.profile'. (17s contre 12s).
Kusalananda
2
@JenniferAnderson J'ai couru les deux à plusieurs reprises. Les 17 et 12 secondes sont des moyennes. Et oui, la grepvariation correspondra n'importe où dans le findrésultat, alors que la correspondance avec find -namene correspondra exactement (dans ce cas).
Kusalananda
2
Oui, ce find filename serait rapide . J'ai supposé que c'était une faute de frappe et que l'OP signifiait find -name filename. Avec find filename, seul filenameserait examiné (et rien d'autre).
Kusalananda

Réponses:

11

(Je suppose que GNU findici)

En utilisant juste

find filename

serait rapide, car il retournerait simplement filename, ou les noms à l'intérieur filenames'il s'agit d'un répertoire, ou une erreur si ce nom n'existait pas dans le répertoire courant. C'est une opération très rapide, similaire à ls filename(mais récursive s'il filenames'agit d'un répertoire).

En revanche,

find | grep filename

permettrait findde générer une liste de tous les noms à partir du répertoire courant et ci-dessous, qui grepfiltrerait ensuite. Ce serait évidemment une opération beaucoup plus lente.

Je suppose que ce qui était réellement prévu était

find . -type f -name 'filename'

Ce serait filenamele nom d'un fichier normal n'importe où dans le répertoire courant ou en dessous.

Ce sera aussi rapide (ou relativement rapide) que find | grep filename, mais la grepsolution correspondrait filenameau chemin complet de chaque nom trouvé, de manière similaire à ce qui -path '*filename*'serait fait avec find.


La confusion vient d'un malentendu sur le findfonctionnement.

L'utilitaire prend un certain nombre de chemins et renvoie tous les noms sous ces chemins.

Vous pouvez ensuite restreindre les noms retournés à l'aide de divers tests qui peuvent agir sur le nom de fichier, le chemin, l'horodatage, la taille du fichier, le type de fichier, etc.

Quand tu dis

find a b c

vous demandez findde lister tous les noms disponibles sous les trois chemins a, bet c. S'il se trouve que ce sont des noms de fichiers normaux dans le répertoire courant, ils seront retournés. Si l'un d'eux se trouve être le nom d'un répertoire, il sera renvoyé avec tous les autres noms à l'intérieur de ce répertoire.

Quand je fais

find . -type f -name 'filename'

Cela génère une liste de tous les noms dans le répertoire courant ( .) et ci-dessous. Ensuite, il restreint les noms à ceux des fichiers normaux, c'est-à-dire pas des répertoires, etc. avec -type f. Ensuite, il y a une autre restriction aux noms qui correspondent à l' filenameutilisation -name 'filename'. La chaîne filenamepeut être un modèle de remplacement de nom de fichier, tel que *.txt(n'oubliez pas de le citer!).

Exemple:

Ce qui suit semble "trouver" le fichier appelé .profiledans mon répertoire personnel:

$ pwd
/home/kk
$ find .profile
.profile

Mais en fait, il retourne juste tous les noms sur le chemin .profile(il n'y a qu'un seul nom, et c'est de ce fichier).

Ensuite, je cdmonte d'un niveau et réessaye:

$ cd ..
$ pwd
/home
$ find .profile
find: .profile: No such file or directory

La findcommande ne trouve plus aucun chemin appelé .profile.

Cependant, si je le fais regarder le répertoire en cours, puis restreindre les noms retournés à seulement.profile , il le trouve également à partir de là:

$ pwd
/home
$ find . -name '.profile'
./kk/.profile
Kusalananda
la source
1
find filenamene retournerait que filenames'il filenamen'était pas de type répertoire (ou était de type répertoire, mais n'avait aucune entrée elle-même)
Stéphane Chazelas
2

Explication non technique: rechercher Jack dans une foule est plus rapide que rechercher tout le monde dans une foule et éliminer tout de la considération sauf Jack.

S Renalds
la source
Le problème est que l'OP s'attend à ce que Jack soit la seule personne dans la foule. Si c'est le cas, ils ont de la chance. find jacklistera jacks'il s'agit d'un fichier appelé jack, ou tous les noms du répertoire s'il s'agit d'un répertoire. C'est une mauvaise compréhension du findfonctionnement.
Kusalananda
1

Je n'ai pas encore compris le problème, mais je peux fournir quelques informations supplémentaires.

Comme pour Kusalananda, l' find | grepappel est clairement plus rapide sur mon système, ce qui n'a pas beaucoup de sens. Au début, j'ai supposé une sorte de problème de mise en mémoire tampon; que l'écriture dans la console ralentit le temps jusqu'au prochain appel système pour lire le nom de fichier suivant. L'écriture sur un tube est très rapide: environ 40 Mo / s même pour des écritures de 32 octets (sur mon système plutôt lent; 300 Mo / s pour une taille de bloc de 1 Mo). J'ai donc supposé que findle système de fichiers peut lire plus rapidement lors de l'écriture dans un canal (ou fichier) afin que les deux opérations de lecture des chemins de fichier et d'écriture sur la console puissent s'exécuter en parallèle (ce qui, en findtant que processus à un seul thread, ne peut pas se faire tout seul.

C'est de findla faute

Comparaison des deux appels

:> time find "$HOME"/ -name '*.txt' >/dev/null

real    0m0.965s
user    0m0.532s
sys     0m0.423s

et

:> time find "$HOME"/ >/dev/null

real    0m0.653s
user    0m0.242s
sys     0m0.405s

montre que cela findfait quelque chose d'incroyablement stupide (quoi que ce soit). Il s'avère juste être assez incompétent à exécuter -name '*.txt'.

Peut dépendre du rapport entrée / sortie

Vous pourriez penser que cela find -namegagne s'il y a très peu à écrire. Mais ça devient plus embarrassant find. Il perd même s'il n'y a rien à écrire contre des fichiers 200K (13M de données de pipe) pour grep:

time find /usr -name lwevhewoivhol

findpeut être aussi rapide que grep, bien que

Il s'avère que cette findstupidité namene s'étend pas aux autres tests. Utilisez plutôt une expression régulière et le problème a disparu:

:> time find "$HOME"/ -regex '\.txt$' >/dev/null     

real    0m0.679s
user    0m0.264s
sys     0m0.410s

Je suppose que cela peut être considéré comme un bug. Quiconque souhaite déposer un rapport de bogue? Ma version est find (GNU findutils) 4.6.0

Hauke ​​Laging
la source
Quelle est la répétabilité de vos horaires? Si vous avez fait le -nametest en premier, il peut avoir été plus lent car le contenu du répertoire n'est pas mis en cache. (Lors des tests -nameet -regexje trouve qu'ils prennent à peu près le même temps, au moins une fois que l'effet de cache a été pris en considération. Bien sûr, il peut s'agir simplement d'une version différente de find...)
psmears
@psmears Bien sûr, j'ai fait ces tests plusieurs fois. Le problème de mise en cache a été mentionné même dans les commentaires de la question avant la première réponse. Ma findversion est find (GNU findutils) 4.6.0
Hauke ​​Laging
Pourquoi est-il surprenant que l'ajout -name '*.txt'ralentisse find? Il doit faire un travail supplémentaire, tester chaque nom de fichier.
Barmar
@Barmar D'une part, ce travail supplémentaire peut être effectué extrêmement rapidement. D'un autre côté, ce travail supplémentaire permet d'économiser d'autres travaux. finddoit écrire moins de données. Et écrire sur un tuyau est une opération beaucoup plus lente.
Hauke ​​Laging du
L'écriture sur un disque est très lente, l'écriture sur un canal n'est pas si mauvaise, elle copie simplement dans un tampon du noyau. Notez que lors de votre premier test, écrire plus pour en /dev/nullquelque sorte utiliser moins de temps système.
Barmar
0

Remarque : je suppose que vous voulez dire find . -name filename(sinon, vous cherchez des choses différentes; find filenameregarde en fait dans un chemin appelé nom de fichier , qui pourrait ne contenir presque aucun fichier, donc sortir très rapidement).


Supposons que vous ayez un répertoire contenant cinq mille fichiers. Sur la plupart des systèmes de fichiers, ces fichiers sont stockés dans un arbre la structure , ce qui permet de localiser rapidement une fichier donné.

Alors , quand vous demandez findde trouver un fichier dont le nom nécessite la vérification, ne findse demande pour ce fichier, et ce fichier uniquement, le système de fichiers sous - jacente, qui va lire très peu de pages de la mémoire de masse. Donc, si le système de fichiers vaut son sel, cette opération s'exécutera beaucoup plus rapidement que de parcourir l'arborescence entière pour récupérer toutes les entrées.

Lorsque vous demandez simplement findmais c'est exactement ce que vous faites, vous parcourez tout l'arbre, en lisant. Chaque. Célibataire. Entrée. Avec de gros répertoires, cela pourrait être un problème (c'est exactement la raison pour laquelle plusieurs logiciels, ayant besoin de stocker de nombreux fichiers sur le disque, créeront des "arborescences de répertoires" de deux ou trois composants en profondeur: de cette façon, chaque feuille ne doit contenir que moins des dossiers).

LSerni
la source
-2

Supposons que le fichier / john / paul / george / ringo / beatles existe et que le fichier que vous recherchez s'appelle «pierres»

find / stones

find comparera les «beatles» aux «pierres» et les déposera lorsque les «s» et les «b» ne correspondent pas.

find / | grep stones

Dans ce cas, find passera '/ john / paul / george / ringo / beatles' à grep et grep devra se frayer un chemin à travers tout le chemin avant de déterminer s'il s'agit d'une correspondance.

grep fait donc beaucoup plus de travail, c'est pourquoi cela prend plus de temps

Paranoïaque
la source
1
L'avez-vous essayé?
Hauke ​​Laging
3
Le coût des comparaisons de chaînes (extrêmement simple et bon marché) est complètement éclipsé par le coût d'E / S (ou tout simplement syscall s'il est mis en cache) des recherches de répertoire.
Mat
grep n'est pas une comparaison de chaînes, sa comparaison d'expressions régulières, ce qui signifie qu'il doit parcourir toute la chaîne jusqu'à ce qu'il trouve une correspondance ou atteigne la fin. Les recherches de répertoire sont les mêmes, peu importe quoi.
Paranoid
@Paranoid Hm, de quelle version de find parlez-vous? Cela ne ressemble apparemment pas à la découverte à laquelle je suis habitué dans Debian.
pipe