Quel est le plus efficace pour trouver les fichiers d'un système de fichiers complet qui contiennent une chaîne: grep récursif ou find avec grep dans une instruction exec? Je suppose que find serait plus efficace car vous pouvez au moins effectuer un filtrage si vous connaissez l'extension du fichier ou une expression régulière qui correspond au nom du 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' {} \;
Je ne suis pas sûr:
grep -r -i 'the brown dog' /*
c'est vraiment ce que vous vouliez dire. Cela signifierait grep récursivement dans tous les fichiers et répertoires non cachés dans /
(Mais toujours regarder à l'intérieur des fichiers cachés et des répertoires à l'intérieur de ceux-ci).
En supposant que vous vouliez dire:
grep -r -i 'the brown dog' /
Quelques points à noter:
grep
ne prennent pas en charge -r
. Et parmi ceux qui le font, les comportements diffèrent: certains suivent les liens symboliques vers les répertoires lors de la traversée de l'arborescence des répertoires (ce qui signifie que vous pouvez finir par regarder plusieurs fois dans le même fichier ou même exécuter en boucles infinies), d'autres non. Certains regarderont à l'intérieur des fichiers de périphérique (et cela prendra un certain temps dans /dev/zero
Par exemple) ou des tuyaux ou des fichiers binaires ..., d'autres non.grep
commence à regarder à l'intérieur des fichiers dès qu'il les découvre. Mais alors qu'il cherche dans un fichier, il ne cherche plus de fichiers à rechercher (ce qui est probablement aussi bien dans la plupart des cas)Votre:
find / -type f -exec grep -i 'the brown dog' {} \;
(supprimé le -r
qui n'avait aucun sens ici) est terriblement inefficace car vous exécutez un grep
par fichier. ;
Ne doit être utilisé que pour les commandes qui n'acceptent qu'un seul argument. De plus ici, parce que grep
ne regarde que dans un seul fichier, il n'imprimera pas le nom du fichier, donc vous ne saurez pas où sont les correspondances.
Vous ne regardez pas à l'intérieur des fichiers de périphérique, des tuyaux, des liens symboliques ..., vous ne suivez pas les liens symboliques, mais vous regardez toujours potentiellement à l'intérieur de choses comme /proc/mem
.
find / -type f -exec grep -i 'the brown dog' {} +
serait beaucoup mieux car le moins de commandes grep
que possible seraient exécutées. Vous obtiendrez le nom du fichier à moins que la dernière exécution n'ait qu'un seul fichier. Pour cela il vaut mieux utiliser:
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é tant que find
n'aura pas trouvé suffisamment de fichiers à mâcher, il y aura donc un certain retard initial. Et find
ne poursuivra pas la recherche de fichiers supplémentaires tant que le précédent grep
ne sera pas revenu. L'allocation et la transmission de la liste des gros fichiers ont un impact (probablement négligeable), donc dans l'ensemble, cela sera probablement moins efficace qu'un grep -r
Qui ne suit pas le lien symbolique ou ne regarde pas à l'intérieur des appareils.
Avec les outils GNU:
find / -type f -print0 | xargs -r0 grep -Hi 'the brown dog'
Comme ci-dessus, le moins d'exemples grep
possibles seront exécutés, mais find
continuera à rechercher plus de fichiers tandis que la première invocation grep
cherchera à l'intérieur du premier lot. Cela peut ou non être un avantage. Par exemple, avec des données stockées sur des disques durs rotatifs, find
et grep
l'accès aux données stockées à différents emplacements sur le disque ralentira le débit du disque en provoquant un déplacement constant de la tête de disque. Dans une configuration RAID (où find
et grep
peuvent accéder à différents disques) ou sur des SSD, cela peut faire une différence positive.
Dans une configuration RAID, l'exécution de plusieurs invocations simultanéesgrep
peut également améliorer les choses. Toujours avec 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 le deuxième grep
ne sera lancé qu'une 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 passer moins de fichiers par grep
invocation).
Notez également que si vous redirigez la sortie xargs
vers autre chose qu'un périphérique terminal, les greps
s commenceront à mettre en mémoire tampon leur sortie, ce qui signifie que la sortie de ces grep
s sera probablement être mal entrelacé. Vous devez utiliser stdbuf -oL
(Si disponible comme sur GNU ou FreeBSD) sur eux pour contourner ce problème (vous pouvez toujours avoir des problèmes avec les très longues lignes (généralement> 4KiB )) ou demandez à chacun d'écrire leur sortie dans un fichier séparé et de les concaténer tous à la fin.
Ici, la chaîne que vous recherchez est fixe (pas une expression rationnelle), donc l'utilisation de l'option -F
Pourrait faire une différence (peu probable car les implémentations grep
savent déjà déjà l'optimiser).
Une autre chose qui pourrait faire une grande différence est de fixer la locale à C si vous êtes dans une locale à plusieurs octets:
find / -type f -print0 | LC_ALL=C xargs -r0 -P2 grep -Hi 'the brown dog'
Pour éviter de regarder à l'intérieur de /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 {} +
Si la *
dans l'appel grep
n'est pas important pour vous, alors la première devrait être plus efficace car une seule instance 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 le *
mais dans les cas Edge, le tri pourrait inverser cela.
Il peut y avoir d'autres structures find
-grep
qui fonctionnent mieux, en particulier avec de nombreux petits fichiers. La lecture de grandes quantités d'entrées de fichier et d'inodes à la fois peut améliorer les performances des supports en rotation.
Mais regardons les statistiques de syscall:
trouver
> 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
Si vous êtes sur un SSD et que le temps de recherche est négligeable, vous pouvez utiliser GNU parallel:
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
a trouvé.
Cela va détruire un disque dur, mais un SSD devrait assez bien y faire face.