Hva er mer effektivt for å finne hvilke filer i et helt filsystem som inneholder en streng: rekursiv grep eller finne med grep i en exec-uttalelse? Jeg antar at finn ville være mer effektivt fordi du i det minste kan gjøre noe filtrering hvis du kjenner filtypen eller en regex som samsvarer med filnavnet, men når du bare vet -type f som er bedre ? GNU grep 2.6.3; finn (GNU findutils) 4.4.2

Eksempel:

grep -r -i "the brown dog" /

find / -type f -exec grep -i "the brown dog" {} \;

Kommentarer

  • Matematikk / informatikk / algoritmeeffektivitet ‘ t mening basert.
  • Sjekk denne. Selv om det ikke er rekursivt, vil det gi en forståelse av hva som er bedre. unix.stackexchange.com/questions/47983/…
  • @AvinashRaj he ‘ ber ikke om mening. Han ‘ ‘ spør hva som er mer effektiv og / eller raskere , ikke hvilken som er » bedre «. Dette er et perfekt svarbart spørsmål som har et enkelt, spesifikt svar som avhenger av hvordan disse to programmene gjør jobben sin, og hva du gir dem til å søke gjennom.
  • Merk at -exec {} + -form vil gjøre færre gafler, så det bør være raskere enn -exec {} \;. Du må kanskje legge til -H (eller -h) i grep -alternativene ekvivalent utgang.
  • Du ønsket sannsynligvis ikke ‘ t -r alternativet på grep for den andre

Svar

Jeg er ikke sikker:

grep -r -i "the brown dog" /* 

er egentlig det du mente. Det vil bety grep rekursivt i alle ikke-skjulte filer og dirs i / (men se fremdeles inne i skjulte filer og dirs inne i disse).

Forutsatt at du mente:

grep -r -i "the brown dog" / 

Noen ting å merke seg:

  • Ikke alle grep implementeringer støtter -r. Og blant de som gjør det, er atferdene forskjellige: noen følger symlenker til kataloger når du krysser katalogtreet (som betyr at du kan ende opp med å se se noen ganger i samme fil eller til og med kjøres i uendelige løkker), vil noen ikke. Noen vil se på enhetsfiler (og det vil ta ganske lang tid i /dev/zero for eksempel) eller rør eller binære filer …, noen vil ikke.
  • Det er effektivt da grep begynner å lete i filer så snart den oppdager dem. Men mens den ser i en fil, ser den ikke lenger etter flere filer å søke i (som er sannsynligvis like bra i de fleste tilfeller)

Din:

find / -type f -exec grep -i "the brown dog" {} \; 

(fjernet -r som ikke ga mening her) er veldig ineffektivt fordi du kjører en grep per fil. ; skal bare brukes til kommandoer som bare godtar ett argument. Dessuten, fordi grep bare ser i en fil, vil den ikke skrive ut filnavnet, så du vet ikke hvor kampene er.

Du » ser ikke på enhetsfiler, rør, symlenker …, du følger ikke symlenker, men du ser fremdeles potensielt på ting som /proc/mem.

find / -type f -exec grep -i "the brown dog" {} + 

ville vært mye bedre fordi så få grep kommandoer som mulig ville kjøres. Du får filnavnet med mindre den siste kjøringen bare har én fil. For det er det bedre å bruke:

find / -type f -exec grep -i "the brown dog" /dev/null {} + 

eller med GNU grep:

find / -type f -exec grep -Hi "the brown dog" {} + 

Merk at grep ikke startes før find har funnet nok filer til at den kan tygge på, så det vil være noen innledende forsinkelse. Og find vil ikke fortsette å søke etter flere filer før forrige grep har returnert. Tildeling og overføring av den store fillisten har noen (sannsynligvis ubetydelig) innvirkning, så alt i alt vil det sannsynligvis være mindre effektivt enn en grep -r som ikke følger symlink eller ser ut inne i enheter.

Med GNU-verktøy:

find / -type f -print0 | xargs -r0 grep -Hi "the brown dog" 

Som ovenfor, så få grep tilfeller som mulig vil kjøres, men find vil fortsette å lete etter flere filer mens den første grep påkallingen ser i den første batchen. Det kan eller ikke kan være en fordel skjønt.For eksempel, med data lagret på rotasjonsharddisker, vil find og grep få tilgang til data som er lagret på forskjellige steder på disken, tregere disken gjennomstrømning ved å få diskhodet til å bevege seg konstant. I et RAID-oppsett (der find og grep kan få tilgang til forskjellige disker) eller på SSD-er, kan det gjøre en positiv forskjell.

I et RAID-oppsett kan det også forbedre ting å kjøre flere samtidige grep. Fortsatt med GNU-verktøy på RAID1-lagring med 3 disker, kan

find / -type f -print0 | xargs -r0 -P2 grep -Hi "the brown dog" 

øke ytelsen betydelig. Vær imidlertid oppmerksom på at den andre grep bare vil startes når det er funnet nok filer til å fylle opp den første grep -kommandoen. Du kan legge til et -n -alternativ til xargs for at det skal skje raskere (og sende færre filer per grep påkallelse).

Vær også oppmerksom på at hvis du omdirigerer xargs -utdata til alt annet enn en terminalenhet, så greps begynner å buffere utdataene sine, noe som betyr at utdataene til de grep er sannsynligvis blir sammenflettet feil. Du må bruke stdbuf -oL (der det er tilgjengelig som på GNU eller FreeBSD) på dem for å omgå det (du kan fremdeles ha problemer med veldig lange linjer (vanligvis> 4KiB)) eller få hver til å skrive utdataene i en egen fil og sammenkoble dem alle til slutt.

Her er strengen du leter etter, løst (ikke en regexp), så bruk av alternativet -F kan gjøre en forskjell (usannsynlig som grep implementeringer vet hvordan du kan optimalisere det allerede).

En annen ting som kan For å gjøre en stor forskjell er å feste lokaliteten til C hvis du er i et multibyte-språk:

find / -type f -print0 | LC_ALL=C xargs -r0 -P2 grep -Hi "the brown dog" 

For å unngå å se på innsiden /proc, /sys …, bruk -xdev og spesifiser filsystemene du vil søke i:

LC_ALL=C find / /home -xdev -type f -exec grep -i "the brown dog" /dev/null {} + 

Eller beskjær stiene du vil ekskludere eksplisitt:

LC_ALL=C find / \( -path /dev -o -path /proc -o -path /sys \) -prune -o \ -type f -exec grep -i "the brown dog" /dev/null {} + 

Kommentarer

  • Jeg ‘ t antar at noen kan peke meg på en ressurs – eller forklare – hva {} og + betyr. Det ‘ er ingenting jeg kan se på mansidene for exec, grep eller finner i Solaris-boksen jeg ‘ bruker. Er det bare skallet som sammenkobler filnavn og sender dem til grep?
  • @Poldie, at ‘ er tydelig forklart i beskrivelsen av -exec predikat i Solaris-mansiden
  • Ah, ja. Jeg slapp ‘ mens jeg søkte på mannssiden. Linken din er bedre; Jeg synes man-sider er forferdelig å lese.
  • RAID1 m / 3 disker? Så rart …
  • @tink, ja RAID1 er på to eller flere disker. Med 3 disker sammenlignet med 2 disker øker du redundansen og leser ytelsen mens skriveytelsen er omtrent den samme. Med 3 disker i motsetning til 2, betyr det at du også kan rette feil, som når du snur litt på en av kopiene, kan du ‘ fortelle hva som er riktig ved å sjekke alt 3 eksemplarer, mens du med to disker kan du ‘ ikke virkelig fortelle.

Svar

Hvis * i grep samtalen ikke er viktig for deg, bør den første være mer effektiv som bare en forekomst av grep er startet, og gafler er ikke gratis. I de fleste tilfeller vil det være raskere selv med * men i kanttilfeller sorteringen kan reversere det.

Det kan være andre findgrep strukturer som fungerer bedre, spesielt med mange små Å lese store mengder filoppføringer og inoder på en gang kan gi ytelsesforbedringer på roterende medier.

Men la oss se på syskallstatistikken:

finn

> 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 

bare 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 

Kommentarer

  • På skalaen til å søke i et helt filsystem er gafler ubetydelige. I / O er det du vil redusere.
  • Selv om det er en feil fra OP, er sammenligningen feil, bør du fjerne -r flagget til grep når du bruker find. Du kan se at den søkte om og om igjen de samme filene ved å sammenligne antall open som skjedde.
  • @qwertzguy, nei, -r bør være ufarlig siden -type f garanterer at ingen av argumentene er kataloger. Flere open() er mer sannsynlig ned til de andre filene som er åpnet av grep ved hver påkalling (biblioteker, lokaliseringsdata …) ( takk for redigeringen på svaret mitt btw)

Svar

Hvis du er på en SSD og søker tid er ubetydelig, kan du bruke GNU parallelt:

find /path -type f | parallel --gnu --workdir "$PWD" -j 8 " grep -i -r "the brown dog" {} " 

Dette vil utføre opptil 8 grep-prosesser samtidig basert på hva find funnet.

Dette vil kaste en harddisk, men en SSD skal takle den ganske bra.

Svar

En ting til å vurdere på denne er som følger.

Vil noen av katalogene som grep må rekursivt gjennomgå, inneholde mer filer enn systemets nofile innstilling? (f.eks. antall åpne filhåndtak, standard er 1024 på de fleste linux-distroer)

I så fall er finn absolutt veien å gå siden visse versjoner av grep vil bombe ut med en Argumentlisten for lang feil når den treffer en katalog med flere filer enn den maksimale åpne filen håndterer innstillingen.

Bare min 2 ¢.

Kommentarer

  • Hvorfor ville grep bombe ut? I det minste med GNU grep hvis du gir en sti med etterfølgende / og bruker -R det ‘ Jeg vil bare gjenta gjennom katalogene. skallet er ikke ‘ vil ikke utvide noe med mindre du gir shell-globs. Så i det gitte eksemplet (/*) er det bare innholdet i / som betyr noe, ikke av undermappene som ganske enkelt blir oppregnet av grep, ikke sendt som argument fra skallet.
  • Vel, med tanke på at OP spurte om å søke rekursivt (f.eks. » grep -r -i ‘ den brune hunden ‘ / * «), har jeg sett GNU ‘ s grep (minst versjon 2.9) bomber ut med: » -bash: / bin / grep: Argumentlisten er for lang » ved å bruke det eksakte søket som OP brukte i en katalog som hadde over 140 000 underkataloger.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *