Vilket är effektivare för att hitta vilka filer i ett helt filsystem som innehåller en sträng: rekursiv grep eller hitta med grep i ett exec-uttalande? Jag antar att hitta skulle vara effektivare eftersom du åtminstone kan filtrera om du känner till filtillägget eller en regex som matchar filnamnet, men när du bara vet -type f
vilket är bättre ? GNU grep 2.6.3; hitta (GNU-sökverktyg) 4.4.2
Exempel:
grep -r -i "the brown dog" /
find / -type f -exec grep -i "the brown dog" {} \;
Kommentarer
Svar
Jag är inte säker:
grep -r -i "the brown dog" /*
är verkligen vad du menade. Det skulle betyda grep rekursivt i alla icke-dolda filer och dirs i /
(men titta ändå inuti dolda filer och dirs inuti dessa).
Förutsatt att du menade:
grep -r -i "the brown dog" /
Några saker att notera:
- Inte alla
grep
implementeringar stöder-r
. Och bland dem som gör det skiljer sig beteenden: vissa följer symlänkar till kataloger när man går igenom katalogträdet (vilket innebär att du kan sluta se flera gånger i samma fil eller till och med köras i oändliga slingor) kommer vissa inte att göra det. Vissa tittar inuti enhetsfiler (och det tar ganska lång tid i/dev/zero
till exempel) eller rör eller binära filer …, vissa inte. - Det är effektivt eftersom
grep
börjar leta inuti filer så snart den upptäcker dem. Men medan den ser i en fil letar den inte längre efter fler filer att söka i (som är antagligen lika bra i de flesta fall)
Din:
find / -type f -exec grep -i "the brown dog" {} \;
(tog bort -r
vilket inte var meningsfullt här) är väldigt ineffektivt eftersom du kör en grep
per fil. ;
bör endast användas för kommandon som endast accepterar ett argument. Dessutom, eftersom grep
bara ser i en fil, kommer det inte att skriva ut filnamnet, så du vet inte var matchningarna är.
Du ” tittar inte inuti enhetsfiler, rör, symlänkar … du följer inte symlänkar, men du tittar fortfarande potentiellt på saker som /proc/mem
.
find / -type f -exec grep -i "the brown dog" {} +
skulle vara mycket bättre eftersom så få grep
kommandon som möjligt skulle köras. Du skulle få filnamnet såvida den sista körningen inte bara har en fil. För det är det bättre att använda:
find / -type f -exec grep -i "the brown dog" /dev/null {} +
eller med GNU grep
:
find / -type f -exec grep -Hi "the brown dog" {} +
Observera att grep
inte startas förrän find
har hittat tillräckligt många filer för att det ska kunna tuggas på, så det kommer att bli en viss initial fördröjning. Och find
fortsätter inte att söka efter fler filer förrän föregående grep
har återvänt. Tilldelning och överföring av den stora fillistan har viss (antagligen försumbar) inverkan, så allt kommer det troligen att vara mindre effektivt än en grep -r
som inte följer symlink eller look inuti enheter.
Med GNU-verktyg:
find / -type f -print0 | xargs -r0 grep -Hi "the brown dog"
Som ovan, så få grep
instanser som möjligt körs, men find
fortsätter att leta efter fler filer medan den första grep
anropet tittar in i den första batchen. Det kan dock inte vara en fördel.Till exempel, med data lagrad på rotationshårddiskar, kommer find
och grep
att få åtkomst till data som lagras på olika platser på disken genom att låta skivhuvudet röra sig ständigt. I en RAID-installation (där find
och grep
kan komma åt olika diskar) eller på SSD-enheter kan det göra en positiv skillnad.
I en RAID-inställning kan det också förbättra saker att köra flera samtidiga grep
anrop. Fortfarande med GNU-verktyg på RAID1-lagring med 3 diskar, kan
find / -type f -print0 | xargs -r0 -P2 grep -Hi "the brown dog"
öka prestandan avsevärt. Observera dock att det andra grep
bara startas när tillräckligt många filer har hittats för att fylla i det första grep
-kommandot. Du kan lägga till ett -n
-alternativ till xargs
för att det ska ske tidigare (och skicka färre filer per grep
anrop).
Observera också att om du omdirigerar xargs
-utdata till allt annat än en terminalenhet, så är greps
kommer att börja buffra deras utdata vilket innebär att utdata från dessa grep
kommer antagligen att vara felaktigt sammanflätade. Du måste använda stdbuf -oL
(om det finns som på GNU eller FreeBSD) på dem för att kringgå det (du kan fortfarande ha problem med mycket långa rader (vanligtvis> 4KiB)) eller låta var och en skriva utdata i en separat fil och sammanfoga dem alla till slut.
Här är strängen du letar efter fixad (inte en regexp) så att alternativet -F
kan göra skillnad (osannolikt som grep
implementeringar vet hur man optimerar det redan.
En annan sak som kan Det skulle göra en stor skillnad att fixa språket till C om du befinner dig i en plats med flera byte:
find / -type f -print0 | LC_ALL=C xargs -r0 -P2 grep -Hi "the brown dog"
För att undvika att titta inuti /proc
, /sys
…, använd -xdev
och ange de filsystem du vill söka i:
LC_ALL=C find / /home -xdev -type f -exec grep -i "the brown dog" /dev/null {} +
Eller beskära de vägar som du vill utesluta uttryckligen:
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
- Jag don ’ t antar att någon kan peka mig på en resurs – eller förklara – vad {} och + betyder. Det finns ’ inget jag kan se på mansidorna för exec, grep eller hitta i Solaris-rutan jag ’ använder. Är det bara skalet som sammanfogar filnamn och skickar dem till grep?
- @Poldie, att ’ förklaras tydligt i beskrivningen av
-exec
predikat i Solaris mansida - Ah, ja. Jag ’ tappade inte min {char medan jag sökte på mansidan. Din länk är bättre; Jag tycker att man-sidor är hemska att läsa.
- RAID1 w / 3 diskar? Så konstigt …
- @tink, ja RAID1 finns på två eller fler diskar. Med 3 diskar jämfört med 2 diskar ökar du redundans och läser prestanda medan skrivprestanda är ungefär densamma. Med 3 skivor i motsats till 2 betyder det att du också kan korrigera fel, som när en bit vänder på en av kopiorna kan du ’ säga vilken som är rätt genom att markera alla 3 kopior medan du med två diskar kan ’ inte riktigt berätta.
Svara
Om *
i grep
samtalet inte är viktigt för dig, bör den första vara effektivare eftersom bara en förekomst av grep
startas och gafflarna är inte fria. I de flesta fall blir det snabbare även med *
men i kantfall sorteringen kan vända det.
Det kan finnas andra find
– grep
strukturer som fungerar bättre särskilt med många små Att läsa stora mängder filposter och inoder på en gång kan ge prestandaförbättring på roterande media.
Men låt oss ta en titt på syscall-statistiken:
hitta
> 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
endast 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å skalan för att söka i ett helt filsystem är gafflar försumbara. I / O är vad du vill minska.
- Även om det är ett fel från OP, är jämförelsen felaktig, du bör ta bort
-r
-flaggan förgrep
när du använderfind
. Du kan se att den sökte om och om igen samma filer genom att jämföra antaletopen
som hände. - @qwertzguy, nej,
-r
borde vara ofarligt eftersom-type f
garanterar att inget av argumenten är kataloger. Multiplaopen()
s är mer sannolikt ned till de andra filerna som öppnas avgrep
vid varje anrop (bibliotek, lokaliseringsdata …) ( tack för redigeringen på mitt svar btw)
Svar
Om du är på en SSD och söker tid är försumbar, du kan använda GNU parallellt:
find /path -type f | parallel --gnu --workdir "$PWD" -j 8 " grep -i -r "the brown dog" {} "
Detta kommer att utföra upp till 8 grep-processer samtidigt baserat på vad find
hittades.
Detta kommer att kasta en hårddisk, men en SSD borde klara det ganska bra.
Svar
Ytterligare en sak att tänka på är den här.
Kommer någon av de kataloger som grep måste rekursivt gå igenom innehålla mer filer än inställningen nofile för ditt system? (t.ex. antal öppna filhandtag, standard är 1024 på de flesta Linux-distros)
Om så är fallet är hitta definitivt vägen att gå eftersom vissa versioner av grep bombar ut med ett Argumentlistan är för lång när den träffar en katalog med fler filer än den maximala öppna filen hanterar inställningen.
Bara mina 2 ¢.
Kommentarer
- Varför skulle
grep
bomba ut? Åtminstone med GNU grep om du ger en sökväg med efterföljande/
och använder-R
det ’ Det går helt enkelt igenom katalogerna. skalet är inte ’ t kommer att expandera någonting om du inte ger shell-globs. Så i det givna exemplet (/*
) är endast innehållet i/
materia, inte av undermapparna som helt enkelt kommer att räknas upp avgrep
, inte skickat som argument från skalet. - Tanke, med tanke på att OP frågade om att söka rekursivt (t.ex. ” grep -r -i ’ den bruna hunden ’ / * ”), har jag sett GNU ’ s grep (åtminstone version 2.9) bombar ut med: ” -bash: / bin / grep: Argumentlistan för lång ” med den exakta sökningen som OP använde i en katalog som hade över 140 000 underkataloger.
-exec {} +
-formuläret gör färre gafflar, så det bör vara snabbare än-exec {} \;
. Du kan behöva lägga till-H
(eller-h
) tillgrep
-alternativen för att få exakt motsvarande utdata.-r
alternativet pågrep
för den andra