Hvilket er mere effektivt til at finde hvilke filer i et helt filsystem der indeholder en streng: rekursiv grep eller find med grep i en exec-sætning? Jeg antager, at find ville være mere effektivt, fordi du i det mindste kan filtrere noget, hvis du kender filtypen eller en regex, der matcher filnavnet, men når du kun kender -type f
, hvilket er bedre ? GNU grep 2.6.3; find (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 virkelig hvad du mente. Det ville betyde grep rekursivt i alle de ikke-skjulte filer og dirs i /
(men stadig se inde i skjulte filer og dirs inde i dem).
Forudsat at du mente:
grep -r -i "the brown dog" /
Et par ting at bemærke:
- Ikke alle
grep
implementeringer understøtter-r
. Og blandt dem der gør det, adfærd adskiller sig: nogle følger symlinks til mapper, når de krydser katalogtræet (hvilket betyder, at du måske ender med at se se ofte i den samme fil eller endda køre i uendelige sløjfer), vil nogle ikke. Nogle vil se inde i enhedsfiler (og det vil tage lang tid i/dev/zero
for eksempel) eller rør eller binære filer … nogle vil ikke. - Det er effektivt, da
grep
begynder at kigge ind i filer, så snart det opdager dem. Men mens det ser i en fil, er det ikke længere på udkig efter flere filer at søge i (som er sandsynligvis lige så godt i de fleste tilfælde)
Din:
find / -type f -exec grep -i "the brown dog" {} \;
(fjernet -r
hvilket ikke giver mening her) er frygtelig ineffektivt, fordi du kører en grep
pr. fil. ;
bør kun bruges til kommandoer, der kun accepterer et argument. Desuden her, fordi grep
kun ser i en fil, vil den ikke udskrive filnavnet, så du ved ikke, hvor kampene er.
Dig ” ser ikke inde i enhedsfiler, rør, symlinks …, du følger ikke symlinks, men du ser stadig potentielt inden i ting som /proc/mem
.
find / -type f -exec grep -i "the brown dog" {} +
ville være meget bedre, fordi så få grep
kommandoer som muligt ville blive kørt. Du får filnavnet, medmindre den sidste kørsel kun har én fil. Til det er det bedre at bruge:
find / -type f -exec grep -i "the brown dog" /dev/null {} +
eller med GNU grep
:
find / -type f -exec grep -Hi "the brown dog" {} +
Bemærk at grep
først startes find
har fundet nok filer til at tygge på, så der vil være en vis forsinkelse. Og find
vil ikke fortsætte med at søge efter flere filer, før den forrige grep
er vendt tilbage. Tildeling og videregivelse af listen over store filer har en vis (sandsynligvis ubetydelig) indvirkning, så alt i alt vil det sandsynligvis være mindre effektiv end en grep -r
, der ikke følger symlink eller ser ud inde i enheder.
Med GNU-værktøjer:
find / -type f -print0 | xargs -r0 grep -Hi "the brown dog"
Som ovenfor, så få grep
forekomster som muligt køres, men find
vil fortsætte med at lede efter flere filer, mens den første grep
indkaldelse kigger inden i den første batch. Det kan dog eller ikke være en fordel.For eksempel med data, der er gemt på rotationsharddiske, find
og grep
, der får adgang til data, der er gemt forskellige steder på disken, sænker disken kapacitet ved at få diskhovedet til at bevæge sig konstant. I en RAID-opsætning (hvor find
og grep
kan få adgang til forskellige diske) eller på SSDer, kan det gøre en positiv forskel.
I en RAID-opsætning kan kørsel af flere samtidige grep
påkald også forbedre tingene. Stadig med GNU-værktøjer på RAID1-lagring med 3 diske,
find / -type f -print0 | xargs -r0 -P2 grep -Hi "the brown dog"
kan øge ydeevnen betydeligt. Bemærk dog, at den anden grep
kun startes, når der er fundet nok filer til at udfylde den første grep
kommando. Du kan tilføje en -n
mulighed til xargs
for at dette skal ske hurtigere (og videregive færre filer pr. grep
invocation).
Bemærk også, at hvis du “omdirigerer xargs
output til alt andet end en terminalenhed, så greps
s begynder at buffere deres output, hvilket betyder, at output fra disse grep
s sandsynligvis vil blive forkert sammenflettet. Du skal bruge stdbuf -oL
(hvor de er tilgængelige som på GNU eller FreeBSD) på dem for at omgå det (du kan stadig have problemer med meget lange linjer (typisk> 4KiB)) eller har hver til at skrive deres output i en separat fil og sammenkæde dem alle i sidste ende.
Her er den streng, du leder efter, rettet (ikke en regexp), så brug af -F
kan muligvis gøre en forskel (usandsynligt som grep
implementeringer ved, hvordan man optimerer det allerede).
En anden ting, der kan Det ville gøre en stor forskel at rette lokaliteten til C, hvis du “er i et multi-byte-sprog:
find / -type f -print0 | LC_ALL=C xargs -r0 -P2 grep -Hi "the brown dog"
For at undgå at kigge ind /proc
, /sys
…, brug -xdev
og angiv de filsystemer, du vil søge i:
LC_ALL=C find / /home -xdev -type f -exec grep -i "the brown dog" /dev/null {} +
Eller beskær de stier, du vil ekskludere eksplicit:
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 formoder, at nogen kan pege mig på en ressource – eller forklare – hvad {} og + betyder. Der ‘ er intet, jeg kan se på mandsiderne til exec, grep eller finde i Solaris-feltet, jeg ‘ bruger. Er kun skallen sammenkædende filnavne og videregiver dem til grep?
- @Poldie, at ‘ forklares tydeligt i beskrivelsen af
-exec
predikat i Solaris-mandsiden - Ah, ja. Jeg var ikke ‘ undslippe min {char mens jeg søgte på mandsiden. Dit link er bedre; Jeg finder mandesider forfærdelige at læse.
- RAID1 m / 3 diske? Hvor mærkeligt …
- @tink, ja RAID1 er på 2 eller flere diske. Med 3 diske sammenlignet med 2 diske øger du redundans og læser ydeevne, mens skriveydelse er omtrent den samme. Med 3 diske i modsætning til 2 betyder det, at du også kan rette fejl, som når en smule vender på en af kopierne, er du ‘ i stand til at fortælle, hvad der er rigtigt ved at markere alle 3 kopier, mens du med 2 diske kan ‘ ikke rigtig fortælle det.
Svar
Hvis *
i grep
-opkaldet ikke er vigtigt for dig, bør den første være mere effektiv, da kun en forekomst af grep
er startet, og gafler er ikke gratis. I de fleste tilfælde vil det være hurtigere selv med *
men i kanttilfælde sorteringen kan vende det.
Der kan være andre find
– grep
strukturer, der fungerer bedre især med mange små Læsning af store mængder filindgange og inoder på én gang kan give en forbedring af ydeevnen på roterende medier.
Men lad os se på syscall-statistikken:
find
> 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
kun 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øgning i et helt filsystem er gafler ubetydelige. I / O er det, du vil reducere.
- Selvom det er en fejl fra OP, er sammenligningen forkert, skal du fjerne
-r
-flagget forgrep
når du brugerfind
. Du kan se, at det søgte igen og igen de samme filer ved at sammenligne antallet afopen
, der skete. - @qwertzguy, nej,
-r
bør være harmløs, da-type f
garanterer, at ingen af argumenterne er mapper. De flereopen()
s er mere sandsynlige ned til de andre filer, der åbnes afgrep
ved hver indkaldelse (biblioteker, lokaliseringsdata …) ( tak for redigeringen på mit svar btw)
Svar
Hvis du er på en SSD og søger tid er ubetydelig, kan du bruge GNU parallel:
find /path -type f | parallel --gnu --workdir "$PWD" -j 8 " grep -i -r "the brown dog" {} "
Dette udfører op til 8 grep-processer på samme tid baseret på hvad find
fundet.
Dette vil kaste en harddisk, men en SSD skal klare det ret godt.
Svar
En ting mere at overveje på denne er som følger.
Vil nogen af de mapper, som grep skal rekursivt gennemgå, indeholde mere filer end dit systems “ nofile indstilling? (f.eks. antal åbne filhåndtag, standard er 1024 på de fleste linux distroer)
Hvis ja, så er find bestemt den rigtige vej, da visse versioner af grep vil bombe ud med en Argumentliste for lang fejl, når den rammer en mappe med flere filer end den maksimale åbne fil håndterer indstilling.
Bare min 2 ¢.
Kommentarer
- Hvorfor ville
grep
bombe ud? I det mindste med GNU grep, hvis du giver en sti med efterfølgende/
og bruger-R
det ‘ Jeg gentager simpelthen gennem telefonbøgerne. -skallen er ikke ‘ t vil udvide noget, medmindre du giver shell-globs. Så i det givne eksempel (/*
) er det kun indholdet af/
, der betyder noget, ikke af undermapperne, som simpelthen vil blive opregnet afgrep
, ikke sendt som argument fra skallen. - Nå, i betragtning af at OP spurgte om at søge rekursivt (f.eks. ” grep -r -i ‘ den brune hund ‘ / * “), har jeg set GNU ‘ s grep (i det mindste version 2.9) bomber ud med: ” -bash: / bin / grep: Argumentlisten er for lang ” ved hjælp af den nøjagtige søgning, som OP anvendte i et bibliotek, der havde over 140.000 underkataloger.
-exec {} +
form vil gøre færre gafler, så det skal være hurtigere end-exec {} \;
. Du skal muligvis tilføje-H
(eller-h
) tilgrep
mulighederne for at få nøjagtigt tilsvarende output.-r
mulighed pågrep
til den anden