Quest-ce qui est le plus efficace pour trouver quels fichiers dans un système de fichiers entier contiennent une chaîne: grep récursif ou find avec grep dans une instruction exec? Je suppose que trouver serait plus efficace car vous pouvez au moins faire un filtrage si vous connaissez lextension de fichier ou une expression régulière qui correspond au nom de fichier, mais lorsque vous ne connaissez que -type f
ce qui est mieux ? GNU grep 2.6.3; find (GNU findutils) 4.4.2
Exemple:
grep -r -i "the brown dog" /
find / -type f -exec grep -i "the brown dog" {} \;
Commentaires
Réponse
Je « ne suis pas sûr:
grep -r -i "the brown dog" /*
est vraiment ce que vous vouliez dire. Cela signifierait grep récursivement dans tous les fichiers et répertoires non cachés dans /
(mais regardez toujours à lintérieur des fichiers et répertoires cachés à lintérieur de ceux-ci).
En supposant que vous vouliez dire:
grep -r -i "the brown dog" /
Quelques points à noter:
- Toutes les implémentations
grep
ne prennent pas en charge-r
. Et parmi ceux qui le font, les comportements diffèrent: certains suivent des liens symboliques vers des répertoires lors de la traversée de larborescence de répertoires (ce qui signifie que vous pouvez finir par chercher plusieurs fois dans le même fichier ou même sexécuter en boucles infinies), certains ne le seront pas. Certains chercheront à lintérieur des fichiers de périphériques (et cela prendra un certain temps dans/dev/zero
par exemple) ou des tubes ou des fichiers binaires …, dautres non. - Cest efficace car
grep
commence à chercher à lintérieur des fichiers dès quil les découvre. Mais alors quil regarde dans un fichier, il ne recherche plus de fichiers à rechercher (ce qui est probablement tout aussi bien dans la plupart des cas)
Votre:
find / -type f -exec grep -i "the brown dog" {} \;
(supprimé le -r
qui na pas de sens ici) est terriblement inefficace car vous » exécutez un grep
par fichier. ;
ne doit être utilisé que pour les commandes qui nacceptent quun seul argument. De plus ici, comme grep
ne regarde que dans un seul fichier, il nimprimera pas le nom du fichier, vous ne saurez donc pas où se trouvent les correspondances.
Vous » vous ne regardez pas à lintérieur des fichiers de périphériques, des tubes, des liens symboliques …, vous ne suivez pas les liens symboliques, mais vous êtes toujours en train de chercher à lintérieur de choses comme /proc/mem
.
find / -type f -exec grep -i "the brown dog" {} +
serait beaucoup mieux car aussi peu de commandes grep
que possible seraient exécutées. Vous « obtiendriez le nom du fichier à moins que la dernière exécution ne contienne un seul fichier. Pour cela, il est préférable dutiliser:
find / -type f -exec grep -i "the brown dog" /dev/null {} +
ou avec GNU grep
:
find / -type f -exec grep -Hi "the brown dog" {} +
Notez que grep
ne sera pas démarré avant le find
a trouvé suffisamment de fichiers pour quil puisse les mâcher, il y aura donc un délai initial. Et find
ne poursuivra pas la recherche de fichiers supplémentaires tant que le grep
précédent ne sera pas retourné. Allouer et transmettre la grande liste de fichiers a un impact (probablement négligeable), donc dans lensemble, cela va probablement être moins efficace quun grep -r
qui ne suit pas le lien symbolique ou ne regarde pas à lintérieur des périphériques.
Avec les outils GNU:
find / -type f -print0 | xargs -r0 grep -Hi "the brown dog"
Comme ci-dessus, aussi peu grep
autant dinstances que possible seront exécutées, mais find
continuera à rechercher plus de fichiers pendant que le premier appel grep
recherche à lintérieur du premier lot. Cela peut être un avantage ou non.Par exemple, avec des données stockées sur des disques durs rotatifs, find
et grep
accéder aux données stockées à différents emplacements sur le disque ralentira le disque débit en faisant bouger constamment la tête de disque. Dans une configuration RAID (où find
et grep
peuvent accéder à différents disques) ou sur des disques SSD, cela peut faire une différence positive.
Dans une configuration RAID, exécuter plusieurs appels simultanés grep
peut également améliorer les choses. Toujours avec les outils GNU sur le stockage RAID1 avec 3 disques,
find / -type f -print0 | xargs -r0 -P2 grep -Hi "the brown dog"
pourrait augmenter considérablement les performances. Notez cependant que la deuxième grep
ne sera lancée quune fois que suffisamment de fichiers auront été trouvés pour remplir la première commande grep
. Vous pouvez ajouter une option -n
à xargs
pour que cela se produise plus tôt (et transmettre moins de fichiers par grep
).
Notez également que si vous « redirigez xargs
la sortie vers autre chose quun périphérique terminal, alors le greps
s commenceront à mettre leur sortie en mémoire tampon, ce qui signifie que la sortie de ces grep
s sera probablement incorrectement entrelacée. Vous devez utiliser stdbuf -oL
(si disponible comme sur GNU ou FreeBSD) sur eux pour contourner cela (vous pouvez toujours avoir des problèmes avec de très longues lignes (généralement> 4KiB)) ou avoir chacun écrire leur sortie dans un fichier séparé et les concaténer tous à la fin.
Ici, la chaîne que vous recherchez est fixe (pas une expression rationnelle) donc lutilisation de loption -F
peut faire une différence (peu probable car grep
les implémentations savent déjà comment loptimiser).
Une autre chose qui ld faire une grande différence est de fixer la locale à C si vous « êtes dans une locale multi-octets:
find / -type f -print0 | LC_ALL=C xargs -r0 -P2 grep -Hi "the brown dog"
Pour éviter de regarder à lintérieur /proc
, /sys
…, utilisez -xdev
et spécifiez les systèmes de fichiers dans lesquels vous souhaitez effectuer la recherche:
LC_ALL=C find / /home -xdev -type f -exec grep -i "the brown dog" /dev/null {} +
Ou élaguez les chemins que vous souhaitez exclure explicitement:
LC_ALL=C find / \( -path /dev -o -path /proc -o -path /sys \) -prune -o \ -type f -exec grep -i "the brown dog" /dev/null {} +
Commentaires
- Je ne ‘ pas supposer que quelquun puisse me diriger vers une ressource – ou expliquer – ce que {} et + signifient. Il ny a ‘ rien que je puisse voir dans les pages de manuel pour exec, grep ou trouver sur la boîte Solaris que je ‘ m en utilisant. Est-ce que le shell concatène simplement les noms de fichiers et les transmet à grep?
- @Poldie, que ‘ est clairement expliqué dans la description du
-exec
prédicat dans la page de manuel Solaris - Ah, oui. Je nai pas ‘ échapper à mon {caractère lors de la recherche dans la page de manuel. Votre lien est meilleur; Je trouve les pages de manuel terribles à lire.
- RAID1 avec 3 disques? Comme cest étrange …
- @tink, oui RAID1 est sur 2 disques ou plus. Avec 3 disques par rapport à 2 disques, vous augmentez la redondance et les performances de lecture tandis que les performances décriture sont à peu près les mêmes. Avec 3 disques au lieu de 2, cela signifie que vous pouvez également corriger les erreurs, comme quand un peu bascule sur lune des copies, vous ‘ pouvez dire ce qui est juste en vérifiant tout 3 copies avec 2 disques, vous pouvez ‘ t vraiment dire.
Réponse
Si le *
dans lappel grep
nest pas important pour vous, le premier devrait être plus efficace quun seul linstance de grep
est démarrée et les fourches ne sont pas gratuites. Dans la plupart des cas, ce sera plus rapide même avec *
mais dans les cas extrêmes le tri pourrait inverser cela.
Il peut y avoir dautres structures find
– grep
qui fonctionnent mieux, surtout avec de nombreux petits La lecture de grandes quantités d’entrées de fichiers et d’inœuds à la fois peut améliorer les performances sur les supports en rotation.
Mais jetons un œil aux statistiques des appels système:
rechercher
> strace -cf find . -type f -exec grep -i -r "the brown dog" {} \; % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 97.86 0.883000 3619 244 wait4 0.53 0.004809 1 9318 4658 open 0.46 0.004165 1 6875 mmap 0.28 0.002555 3 977 732 execve 0.19 0.001677 2 980 735 stat 0.15 0.001366 1 1966 mprotect 0.09 0.000837 0 1820 read 0.09 0.000784 0 5647 close 0.07 0.000604 0 5215 fstat 0.06 0.000537 1 493 munmap 0.05 0.000465 2 244 clone 0.04 0.000356 1 245 245 access 0.03 0.000287 2 134 newfstatat 0.03 0.000235 1 312 openat 0.02 0.000193 0 743 brk 0.01 0.000082 0 245 arch_prctl 0.01 0.000050 0 134 getdents 0.00 0.000045 0 245 futex 0.00 0.000041 0 491 rt_sigaction 0.00 0.000041 0 246 getrlimit 0.00 0.000040 0 489 244 ioctl 0.00 0.000038 0 591 fcntl 0.00 0.000028 0 204 188 lseek 0.00 0.000024 0 489 set_robust_list 0.00 0.000013 0 245 rt_sigprocmask 0.00 0.000012 0 245 set_tid_address 0.00 0.000000 0 1 uname 0.00 0.000000 0 245 fchdir 0.00 0.000000 0 2 1 statfs ------ ----------- ----------- --------- --------- ---------------- 100.00 0.902284 39085 6803 total
grep uniquement
> strace -cf grep -r -i "the brown dog" . % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 40.00 0.000304 2 134 getdents 31.71 0.000241 0 533 read 18.82 0.000143 0 319 6 openat 4.08 0.000031 4 8 mprotect 3.29 0.000025 0 199 193 lseek 2.11 0.000016 0 401 close 0.00 0.000000 0 38 19 open 0.00 0.000000 0 6 3 stat 0.00 0.000000 0 333 fstat 0.00 0.000000 0 32 mmap 0.00 0.000000 0 4 munmap 0.00 0.000000 0 6 brk 0.00 0.000000 0 2 rt_sigaction 0.00 0.000000 0 1 rt_sigprocmask 0.00 0.000000 0 245 244 ioctl 0.00 0.000000 0 1 1 access 0.00 0.000000 0 1 execve 0.00 0.000000 0 471 fcntl 0.00 0.000000 0 1 getrlimit 0.00 0.000000 0 1 arch_prctl 0.00 0.000000 0 1 futex 0.00 0.000000 0 1 set_tid_address 0.00 0.000000 0 132 newfstatat 0.00 0.000000 0 1 set_robust_list ------ ----------- ----------- --------- --------- ---------------- 100.00 0.000760 2871 466 total
Commentaires
- A léchelle de la recherche dun système de fichiers entier, les fourchettes sont négligeables. Les E / S sont ce que vous voulez réduire.
- Bien quil sagisse dune erreur de lOP, la comparaison est incorrecte, vous devez supprimer lindicateur
-r
degrep
lors de lutilisation defind
. Vous pouvez voir quil a recherché à maintes reprises les mêmes fichiers en comparant le nombre deopen
qui se sont produits. - @qwertzguy, non, le
-r
devrait être inoffensif car le-type f
garantit quaucun des arguments nest des répertoires. Les multiplesopen()
s sont plus probablement dus aux autres fichiers ouverts pargrep
à chaque invocation (bibliothèques, données de localisation …) ( merci pour la modification de ma réponse btw)
Réponse
Si vous « êtes sur un SSD et cherchez le temps est négligeable, vous pouvez utiliser GNU parallèle:
find /path -type f | parallel --gnu --workdir "$PWD" -j 8 " grep -i -r "the brown dog" {} "
Cela exécutera jusquà 8 processus grep en même temps en fonction de ce que find
trouvé.
Cela va écraser un disque dur, mais un SSD devrait plutôt bien sen sortir.
Réponse
Une autre chose à considérer sur celui-ci est la suivante.
Est-ce que lun des répertoires que grep devra parcourir récursivement en contiendra plus fichiers que le paramètre nofile de votre système? (par exemple, le nombre de descripteurs de fichiers ouverts, la valeur par défaut est 1024 sur la plupart des distributions Linux)
Si tel est le cas, find est définitivement la voie à suivre puisque certaines versions de grep bombardera avec une erreur Liste darguments trop longue quand il atteint un répertoire avec plus de fichiers que le fichier ouvert maximum gère le réglage.
Juste mon 2 ¢.
Commentaires
- Pourquoi
grep
bombe? Au moins avec GNU grep si vous donnez un chemin avec/
et utilisez-R
it ‘ ll simplement parcourir les répertoires. Le shell nest ‘ pas de développer quoi que ce soit à moins que vous ne donniez des shell-globs. Donc, dans lexemple donné (/*
), seul le contenu de/
compte, et non des sous-dossiers qui seront simplement énumérés pargrep
, non passé comme argument depuis le shell. - Eh bien, étant donné que lOP demandait une recherche récursive (par exemple » grep -r -i ‘ le chien brun ‘ / * « ), jai vu GNU ‘ s grep (au moins la version 2.9) bombe avec: » -bash: / bin / grep: Liste darguments trop longue » en utilisant la recherche exacte utilisée par lOP sur un répertoire contenant plus de 140 000 sous-répertoires.
-exec {} +
fera moins de fourches, donc devrait être plus rapide que-exec {} \;
. Vous devrez peut-être ajouter-H
(ou-h
) aux optionsgrep
pour obtenir exactement résultat équivalent.-r
surgrep
pour le second