Jeg gjorde et veldig enkelt søk:
grep -R Milledgeville ~/Documents
Og etter en stund dukket denne feilen opp:
grep: memory exhausted
Hvordan kan jeg unngå dette?
Jeg har 10 GB RAM på systemet og få applikasjoner kjører, så jeg er veldig overrasket over at en enkel grep går tom for minne. ~/Documents
handler om 100 GB og inneholder alle slags filer.
grep -RI
har kanskje ikke dette problemet, men jeg vil for å søke i binære filer også.
Svar
To potensielle problemer:
-
grep -R
(bortsett fra den modifiserte GNUgrep
funnet på OS / X 10.8 og nyere) følger symlenker, så selv om det bare er 100 GB filer i~/Documents
, det kan fortsatt være en symlink til/
for eksempel, og du vil ende opp med å skanne hele filsystemet inkludert filer som/dev/zero
. Brukgrep -r
med nyere GNUgrep
, eller bruk standardsyntaks:find ~/Documents -type f -exec grep Milledgeville /dev/null {} +
(men vær oppmerksom på at utgangsstatusen ikke gjenspeiler det faktum at mønsteret samsvarer eller ikke).
-
grep
finner linjene som samsvarer med mønsteret. For det må den laste en linje av gangen i minnet. GNUgrep
i motsetning til mange andregrep
implementeringer har ikke en grense for størrelsen på linjene den leser og støtter søk i binære filer. Så hvis du har en fil med en veldig stor linje (det vil si med to newline-tegn veldig langt fra hverandre), større enn tilgjengelig minne, vil den mislykkes.Det vil vanligvis skje med sparsom fil. Du kan reprodusere den med:
truncate -s200G some-file grep foo some-file
Den ene er vanskelig å omgå. Du kan gjøre det som (fremdeles med GNU
grep
):find ~/Documents -type f -exec sh -c "for i do tr -s "\0" "\n" < "$i" | grep --label="$i" -He "$0" done" Milledgeville {} +
Som konverterer sekvenser av NUL-tegn til ett nytt linjetegn før innmatingen mates til
grep
. Dette dekker tilfeller der problemet skyldes sparsomme filer.Du kan optimalisere det ved å gjøre det bare for store filer:
find ~/Documents -type f \( -size -100M -exec \ grep -He Milledgeville {} + -o -exec sh -c "for i do tr -s "\0" "\n" < "$i" | grep --label="$i" -He "$0" done" Milledgeville {} + \)
Hvis filene er ikke sparsomme og du har en versjon av GNU
grep
før2.6
, du kan bruke alternativet--mmap
. Linjene blir kartlagt i minnet i motsetning til kopiert der, noe som betyr at systemet alltid kan gjenvinne minnet y ved å søke ut sidene til filen. Dette alternativet ble fjernet i GNUgrep
2.6
Kommentarer
Svar
Jeg pleier å gjøre
find ~/Documents | xargs grep -ne "expression"
Jeg prøvde en rekke metoder, og fant dette å være den raskeste. Merk at dette ikke håndterer filer med mellomrom filnavnet veldig bra. Hvis du vet at dette er tilfelle og har en GNU-versjon av grep, kan du bruke:
find ~/Documents -print0 | xargs -0 grep -ne "expression"
Hvis ikke, kan du bruke:
find ~/Documents -exec grep -ne "expression" "{}" \;
Hvilket vil exec
en grep for hver fil.
Kommentarer
- Dette vil bryte på filer med mellomrom.
- Hmm, det stemmer.
- Du kan komme deg rundt det med
find -print0 | xargs -0 grep -ne 'expression'
- @ChrisDown heller en ikke-beskyttende løsning enn en ødelagt bærbar løsning.
- @ChrisDown Most store enheter har vedtatt
find -print0
ogxargs -0
nå: alle tre BSD, MINIX 3, Solaris 11,…
Svar
Jeg kan tenke meg noen måter å komme meg rundt:
-
I stedet å ta tak i alle filene samtidig, gjør en fil om gangen.Eksempel:
find /Documents -type f -exec grep -H Milledgeville "{}" \;
-
Hvis du bare trenger å vite hvilke filer som inneholder ordene, gjør du
grep -l
i stedet. Siden grep slutter å søke etter første treff, trenger den ikke å fortsette å lese store filer -
Hvis du også vil ha den faktiske teksten, kan du streng to separate grep langs:
for file in $( grep -Rl Milledgeville /Documents ); do grep -H Milledgeville "$file"; done
Kommentarer
- Siste eksempel er ikke gyldig syntaks – du ‘ d trenger å utføre en kommandosubstitusjon (og du bør ikke ‘ t gjøre det, siden
grep
utganger ved hjelp av en avgrenser som er lovlig i filnavn). Du må også sitere$file
. - Det siste eksemplet lider med problemet med filnavn som har ny linje eller mellomrom, (det vil føre til at
for
behandler filen som to argumenter) - @DravSloan Din redigering, mens en forbedring, bryter fremdeles med juridiske filnavn.
- Ja, jeg la det være fordi det var en del av svaret hennes, jeg prøvde bare å forbedre det slik at det ville kjøre (for tilfeller er ingen mellomrom / nye linjer osv. i filer).
- Rettelser av hans – > henne, jeg beklager Jenny: /
Svar
Jeg tar tak i en 6 TB disk for å søke etter tapte data, og fikk minnet utmattet -feil. Dette burde også fungere for andre filer.
Løsningen vi kom på var å lese disken i biter ved å bruke dd, og gripe biter. Dette er koden (big-grep.sh):
#problem: grep gives "memory exhausted" error on 6TB disks #solution: read it on parts if [ -z $2 ] || ! [ -e $1 ]; then echo "$0 file string|less -S # greps in chunks"; exit; fi FILE="$1" MATCH="$2" SIZE=`ls -l $1|cut -d\ -f5` CHUNKSIZE=$(( 1024 * 1024 * 1 )) CHUNKS=100 # greps in (100 + 1) x 1MB = 101MB chunks COUNT=$(( $SIZE / $CHUNKSIZE * CHUNKS )) for I in `seq 0 $COUNT`; do dd bs=$CHUNKSIZE skip=$(($I*$CHUNKS)) count=$(( $CHUNKS+1)) if=$FILE status=none|grep -UF -a --context 6 "$MATCH" done
Kommentarer
- Med mindre du leser overlappende biter, vil du muligens savne kamper på klumpegrensene. Overlappingen må være minst like stor som strengen du forventer å matche.
- Oppdatert for å søke 1 MB ekstra i hver 100 MB del … billig hack
grep
kaste bufferne den har behandlet så langt. Du kangrep
utgangen frayes
på ubestemt tid uten å bruke mer enn noen få kilobyte minne. Problemet er størrelsen på linjene.--null-data
kan også være nyttig her. Det tvinger bruken av NUL i stedet for newline som en inngangslinjeterminator.dd if=/dev/sda | fold -b $((4096*1024*1024)) | grep -a "some string"
for å begrense minnemengden som kreves til 4 GB