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

  • Matematik / datavetenskap / algoritmeffektivitet ins ’ t yttrande baserat.
  • Kontrollera den här. Även om det inte är rekursivt, skulle det ge en förståelse för vilken som är bättre. unix.stackexchange.com/questions/47983/…
  • @AvinashRaj he ’ frågar inte om yttrande. Han ’ ’ frågar vilken som är mer effektiv och / eller snabbare , inte vilken som är ” bättre ”. Det här är en perfekt svarbar fråga som har ett enda, specifikt svar som beror på hur dessa två program gör sitt jobb och på exakt vad du ger dem att söka igenom.
  • Observera att -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) till grep -alternativen för att få exakt motsvarande utdata.
  • Du ville antagligen inte ’ t -r alternativet på grep för den andra

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 findgrep 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ör grep när du använder find. Du kan se att den sökte om och om igen samma filer genom att jämföra antalet open som hände.
  • @qwertzguy, nej, -r borde vara ofarligt eftersom -type f garanterar att inget av argumenten är kataloger. Multipla open() s är mer sannolikt ned till de andra filerna som öppnas av grep 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 av grep, 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.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *