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
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 find
– grep
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 tilgrep
når du brukerfind
. Du kan se at den søkte om og om igjen de samme filene ved å sammenligne antallopen
som skjedde. - @qwertzguy, nei,
-r
bør være ufarlig siden-type f
garanterer at ingen av argumentene er kataloger. Flereopen()
er mer sannsynlig ned til de andre filene som er åpnet avgrep
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 avgrep
, 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.
-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
) igrep
-alternativene ekvivalent utgang.-r
alternativet pågrep
for den andre