Wat is efficiënter om te vinden welke bestanden in een volledig bestandssysteem een string bevatten: recursieve grep of vind met grep in een exec-instructie? Ik neem aan dat find efficiënter zou zijn, omdat je op zijn minst wat kunt filteren als je de bestandsextensie kent of een regex die overeenkomt met de bestandsnaam, maar als je alleen -type f
kent, wat beter is ? GNU grep 2.6.3; find (GNU findutils) 4.4.2
Voorbeeld:
grep -r -i "the brown dog" /
find / -type f -exec grep -i "the brown dog" {} \;
Reacties
Antwoord
Ik “weet het niet zeker:
grep -r -i "the brown dog" /*
is echt wat je bedoelde. Dat zou recursief grep betekenen in alle niet-verborgen bestanden en mappen in /
(maar kijk nog steeds in verborgen bestanden en mappen erin).
Aangenomen dat je bedoelde:
grep -r -i "the brown dog" /
Een paar dingen om op te merken:
- Niet alle
grep
implementaties ondersteunen-r
. En onder degenen die dat wel doen, verschillen de gedragingen: sommige volgen symbolische links naar mappen wanneer ze de mappenboom doorlopen (wat betekent dat u er veral tijden in hetzelfde bestand of zelfs in oneindige lussen), sommige niet. Sommigen zullen in apparaatbestanden kijken (en het zal behoorlijk wat tijd kosten in bijvoorbeeld/dev/zero
) of pipes of binaire bestanden …, andere niet. - Het is efficiënt omdat
grep
in bestanden begint te zoeken zodra het ze ontdekt. Maar terwijl het in een bestand kijkt, zoekt het niet langer naar meer bestanden om in te zoeken (die is waarschijnlijk net zo goed in de meeste gevallen)
Uw:
find / -type f -exec grep -i "the brown dog" {} \;
(verwijderde de -r
wat hier niet” klopte) is vreselijk inefficiënt omdat je “één grep
per bestand uitvoert. ;
mag alleen worden gebruikt voor opdrachten die slechts één argument accepteren. Bovendien, omdat grep
slechts in één bestand kijkt, zal het de bestandsnaam niet afdrukken, dus u weet “niet waar de overeenkomsten zijn.
Jij” je kijkt niet in apparaatbestanden, pipes, symlinks …, je “volgt geen symlinks, maar je” kijkt mogelijk nog steeds in dingen als /proc/mem
.
find / -type f -exec grep -i "the brown dog" {} +
zou veel beter zijn omdat zo min mogelijk grep
commandos zouden worden uitgevoerd. Je krijgt de bestandsnaam tenzij de laatste run maar één bestand heeft. Daarvoor is het beter om te gebruiken:
find / -type f -exec grep -i "the brown dog" /dev/null {} +
of met GNU grep
:
find / -type f -exec grep -Hi "the brown dog" {} +
Merk op dat grep
niet zal worden gestart totdat find
heeft genoeg bestanden gevonden om erop te kauwen, dus er zal een eerste vertraging zijn. En find
zal niet doorgaan met zoeken naar meer bestanden totdat de vorige grep
is teruggekeerd. Het toewijzen en doorgeven van de grote bestandenlijst heeft een (waarschijnlijk verwaarloosbare) impact, dus al met al zal het waarschijnlijk minder efficiënt zijn dan een grep -r
die geen symlink volgt of kijkt binnen apparaten.
Met GNU-tools:
find / -type f -print0 | xargs -r0 grep -Hi "the brown dog"
Zoals hierboven, zo weinig grep
mogelijke instanties worden uitgevoerd, maar find
zal blijven zoeken naar meer bestanden terwijl de eerste grep
aanroep in de eerste batch kijkt. Dat kan echter wel of niet een voordeel zijn.Met bijvoorbeeld gegevens die zijn opgeslagen op roterende harde schijven, find
en grep
toegang krijgen tot gegevens die op verschillende locaties op de schijf zijn opgeslagen, zal de schijf langzamer worden doorvoer door ervoor te zorgen dat de schijfkop constant beweegt. In een RAID-configuratie (waarbij find
en grep
toegang hebben tot verschillende schijven) of op SSDs, kan dat een positief verschil maken.
In een RAID-opstelling kan het uitvoeren van verschillende gelijktijdige grep
aanroepen ook dingen verbeteren. Nog steeds met GNU-tools op RAID1-opslag met 3 schijven,
find / -type f -print0 | xargs -r0 -P2 grep -Hi "the brown dog"
zou de prestatie aanzienlijk kunnen verhogen. Merk echter op dat de tweede grep
pas zal worden gestart als er voldoende bestanden zijn gevonden om het eerste grep
commando te vullen. U kunt een -n
optie aan xargs
toevoegen om dat eerder te laten gebeuren (en minder bestanden door te geven per grep
aanroep).
Merk ook op dat als u “xargs
uitvoer naar iets anders dan een eindapparaat omleidt, de greps
s zullen beginnen met het bufferen van hun uitvoer, wat betekent dat de uitvoer van die grep
s waarschijnlijk onjuist interleaved zal zijn. Je “zou moeten gebruiken stdbuf -oL
(waar beschikbaar zoals op GNU of FreeBSD) om dat te omzeilen (je hebt misschien nog steeds problemen met erg lange regels (meestal> 4KiB)) of laat elk hun uitvoer in een apart bestand schrijven en ze allemaal samenvoegen op het einde.
Hier is de string waarnaar u zoekt vast (geen regexp), dus het gebruik van de optie -F
kan een verschil maken (onwaarschijnlijk omdat grep
implementaties weten al hoe ze dat kunnen optimaliseren).
Nog iets dat kan ld maakt een groot verschil door de locale te corrigeren naar C als je “in een multi-byte locale bent:
find / -type f -print0 | LC_ALL=C xargs -r0 -P2 grep -Hi "the brown dog"
Om te voorkomen dat je in /proc
, /sys
…, gebruik -xdev
en specificeer de bestandssystemen waarin je wilt zoeken:
LC_ALL=C find / /home -xdev -type f -exec grep -i "the brown dog" /dev/null {} +
Of snoei de paden die u expliciet wilt uitsluiten:
LC_ALL=C find / \( -path /dev -o -path /proc -o -path /sys \) -prune -o \ -type f -exec grep -i "the brown dog" /dev/null {} +
Reacties
- Ik denk niet dat ‘ niet kan veronderstellen dat iemand me naar een bron kan verwijzen – of kan uitleggen – wat {} en + betekenen. Er is ‘ niets dat ik kan zien in de man-paginas voor exec, grep of vind in de Solaris-box die ik ‘ gebruik. Is alleen de shell die bestandsnamen aaneenschakelt en ze doorgeeft aan grep?
- @Poldie, dat ‘ s duidelijk uitgelegd bij de beschrijving van de
-exec
predikaat in de Solaris-manpagina - Ah, ja. Ik kon ‘ niet ontsnappen aan mijn {char tijdens het zoeken in de man-pagina. Uw link is beter; Ik vind manpages verschrikkelijk om te lezen.
- RAID1 met 3 schijven? Hoe vreemd …
- @tink, ja RAID1 staat op 2 of meer schijven. Met 3 schijven in vergelijking met 2 schijven, verhoog je de redundantie en leesprestaties terwijl de schrijfprestaties ongeveer hetzelfde zijn. Met 3 schijven in plaats van 2, betekent dit dat u ook fouten kunt corrigeren, want als een van de kopieën een beetje omdraait, ‘ kunt u zien welke de juiste is door alle 3 kopieën terwijl je met 2 schijven ‘ niet echt kunt vertellen.
Antwoord
Als de *
in de grep
oproep niet belangrijk voor je is, dan zou de eerste efficiënter moeten zijn, aangezien de enige instantie van grep
is gestart, en vorken zijn “niet gratis. In de meeste gevallen zal het zelfs sneller zijn met de *
maar in randgevallen het sorteren kan dat omkeren.
Er kunnen andere find
– grep
structuren zijn die beter werken, vooral met veel kleine bestanden. Het in één keer lezen van grote hoeveelheden bestandsingangen en inodes kan een prestatieverbetering opleveren op roterende media.
Maar laten we eens kijken naar de syscall-statistieken:
vind
> 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
alleen grep
> 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
Reacties
- Op de schaal van het doorzoeken van een volledig bestandssysteem, zijn forks verwaarloosbaar. I / O is wat u wilt verminderen.
- Hoewel het een fout is van het OP, is de vergelijking onjuist, u moet de
-r
vlag vangrep
bij gebruik vanfind
. U kunt zien dat het keer op keer in dezelfde bestanden heeft gezocht door het aantalopen
dat is gebeurd te vergelijken. - @qwertzguy, nee, de
-r
zou onschadelijk moeten zijn aangezien de-type f
garandeert dat geen van de argumenten mappen zijn. De meerdereopen()
s zijn waarschijnlijker dan de andere bestanden die worden geopend doorgrep
bij elke aanroep (bibliotheken, lokalisatiegegevens …) ( bedankt voor het bewerken van mijn antwoord trouwens)
Antwoord
Als je op een SSD zit en tijd zoekt verwaarloosbaar is, zou je GNU parallel kunnen gebruiken:
find /path -type f | parallel --gnu --workdir "$PWD" -j 8 " grep -i -r "the brown dog" {} "
Dit zal tot 8 grep-processen tegelijkertijd uitvoeren op basis van wat find
gevonden.
Dit zal een harde schijf verpletteren, maar een SSD zou er redelijk goed mee om moeten gaan.
Antwoord
Nog een ding om te overwegen bij deze is als volgt.
Zal een van de mappen die grep recursief moet doorlopen, meer bevatten bestanden dan de nofile -instelling van uw systeem? (bijv. aantal geopende bestandshandvatten, standaard is 1024 op de meeste Linux-distributies)
Als dat zo is, dan is find zeker de juiste keuze aangezien bepaalde versies van grep zal bombarderen met een Argumentenlijst te lang fout wanneer het een map treft met meer bestanden dan het maximaal geopende bestand behandelt de instelling.
Alleen mijn 2 ¢.
Reacties
- Waarom zou
grep
bombarderen? Tenminste met GNU grep als je een pad geeft met achteraan/
en-R
gebruikt ‘ ll gewoon door de mappen. De shell zal niet ‘ iets uitbreiden, tenzij je shell-globs geeft. Dus in het gegeven voorbeeld (/*
) is alleen de inhoud van/
van belang, niet van de submappen die simpelweg worden opgesomd doorgrep
, niet doorgegeven als argument van de shell. - Welnu, aangezien het OP vroeg om recursief te zoeken (bijv. ” grep -r -i ‘ de bruine hond ‘ / * “), heb ik gezien GNU ‘ s grep (ten minste versie 2.9) bombarderen met: ” -bash: / bin / grep: Argumentenlijst te lang ” met de exacte zoekopdracht die het OP gebruikte voor een directory met meer dan 140.000 sub-directories.
-exec {} +
form zal minder forks doen, dus zou sneller moeten zijn dan-exec {} \;
. Mogelijk moet u-H
(of-h
) toevoegen aan degrep
opties om exact gelijkwaardige output.-r
optie opgrep
voor de tweede