Căutam foarte simplu:

grep -R Milledgeville ~/Documents 

Și după ceva timp a apărut această eroare:

grep: memory exhausted 

Cum pot evita acest lucru?

Am 10 GB RAM pe sistemul meu și puține aplicații rulează, așa că sunt cu adevărat surprins că un simplu grep rămâne fără memorie. ~/Documents are aproximativ 100 GB și conține tot felul de fișiere.

grep -RI s-ar putea să nu aibă această problemă, dar vreau să căutați și în fișiere binare.

Răspundeți

Două probleme potențiale:

  • grep -R (cu excepția GNU modificat grep găsit pe OS / X 10.8 și mai sus) urmează linkuri simbolice, deci chiar dacă există doar „s 100 GB de fișiere în ~/Documents, s-ar putea să existe în continuare un link simbolic către / de exemplu și veți termina scanarea întregului sistem de fișiere, inclusiv fișiere precum /dev/zero. Utilizați grep -r cu GNU mai nou grep sau utilizați sintaxa standard:

    find ~/Documents -type f -exec grep Milledgeville /dev/null {} + 

    (cu toate acestea, rețineți că starea de ieșire nu va reflecta faptul că modelul este sau nu egal).

  • grep găsește liniile care se potrivesc cu modelul. Pentru aceasta, trebuie să încarce câte o linie în memorie. GNU grep spre deosebire de multe alte grep implementările nu au o limită pentru mărimea liniilor pe care le citește și acceptă căutarea în fișiere binare. Deci, dacă aveți un fișier cu o linie foarte mare (adică cu două caractere de linie nouă foarte departe), mai mare decât memoria disponibilă, va eșua.

    Acest lucru s-ar întâmpla de obicei cu un fișier rar. Puteți să-l reproduceți cu:

    truncate -s200G some-file grep foo some-file 

    Acela este dificil de rezolvat. Puteți face acest lucru așa (încă cu GNU grep):

    find ~/Documents -type f -exec sh -c "for i do tr -s "\0" "\n" < "$i" | grep --label="$i" -He "$0" done" Milledgeville {} + 

    Aceasta convertește secvențe de caractere NUL într-un singur caracter de linie nouă înainte de a alimenta intrarea către grep. Aceasta ar acoperi cazurile în care problema se datorează fișierelor rare.

    Puteți să o optimizați făcând-o doar pentru fișierele mari:

    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 {} + \) 

    Dacă fișierele sunt nu rare și aveți o versiune a GNU grep înainte de 2.6, puteți utiliza opțiunea --mmap. Liniile vor fi mmapate în memorie spre deosebire de copiate acolo, ceea ce înseamnă că sistemul poate revendica oricând memoria y prin paginarea paginilor în fișier. Această opțiune a fost eliminată în GNU grep 2.6

Comentarii

  • @GodricSeer, poate citi în continuare o mare parte din fișier într-un singur buffer, dar dacă nu are ‘ nu găsește șirul acolo și nu are ‘ Nu am găsit nici un caracter de linie nouă, pariul meu este că păstrează acel memorie tampon în memorie și citește următorul buffer, deoarece va trebui să-l afișeze dacă se găsește o potrivire. Deci, problema rămâne aceeași. În practică, grep-ul pe un fișier rar de 200 GB eșuează cu OOM.
  • @GodricSeer, bine nu. Dacă liniile sunt toate mici, grep poate arunca tampoanele pe care le-a procesat până acum. Puteți grep ieșirea yes la nesfârșit fără a utiliza mai mult de câțiva kiloocteți de memorie. Problema este dimensiunea liniilor.
  • Opțiunea GNU grep --null-data poate fi utilă și aici. Forțează utilizarea NUL în loc de linie nouă ca terminator de linie de intrare.
  • @ 1_CR, punct bun, deși asta setează și terminatorul liniei de ieșire la NUL.
  • Ar fi fold ajutor de comandă în aceste situații? De exemplu, gândiți-vă la dd if=/dev/sda | fold -b $((4096*1024*1024)) | grep -a "some string" pentru a limita cantitatea de memorie necesară la 4 GB

Răspundeți

De obicei,

find ~/Documents | xargs grep -ne "expression" 

Am încercat o grămadă de metode și am găsit că aceasta este cea mai rapidă. Rețineți că acest lucru nu gestionează foarte bine fișierele cu spații pentru numele fișierului. Dacă știți că este cazul și aveți o versiune GNU de grep, puteți utiliza:

find ~/Documents -print0 | xargs -0 grep -ne "expression" 

Dacă nu, puteți utiliza:

 find ~/Documents -exec grep -ne "expression" "{}" \; 

Care va exec un grep pentru fiecare fișier.

Comentarii

  • Acest lucru se va sparge fișierelor cu spații.
  • Hmm, este adevărat.
  • Puteți evita acest lucru cu find -print0 | xargs -0 grep -ne 'expression'
  • @ChrisDown mai degrabă o soluție care nu poate fi protejată decât o soluție portabilă defectă.
  • @ChrisDown Majoritatea unitățile majore au adoptat până acum find -print0 și xargs -0: toate cele trei BSD, MINIX 3, Solaris 11, …

Răspuns

Mă pot gândi la câteva moduri de a evita acest lucru:

  • În schimb de a agrava toate fișierele simultan, faceți câte un fișier la un moment dat.Exemplu:

    find /Documents -type f -exec grep -H Milledgeville "{}" \; 
  • Dacă trebuie doar să știți ce fișiere conțin cuvintele, faceți grep -l în schimb. Deoarece grep nu va mai căuta după prima lovitură, nu va trebui să citiți în continuare niciun fel de fișiere uriașe

  • Dacă doriți și textul real, puteți înșira două separate greps along:

    for file in $( grep -Rl Milledgeville /Documents ); do grep -H Milledgeville "$file"; done 

Comentarii

  • Ultimul exemplu sintaxa nu este validă – ‘ trebuie să efectuați o înlocuire a comenzii (și nu ar trebui să ‘ nu faceți acest lucru, deoarece grep rezultă utilizând un delimitator legal în numele fișierelor). De asemenea, trebuie să citați $file.
  • Cel din urmă exemplu suferă cu problema numelor de fișiere care conțin linie nouă sau spațiu alb, (va face ca for să proceseze fișierul ca două argumente)
  • @DravSloan Editarea dvs., în timp ce o îmbunătățire, încă se rupe pe numele fișierelor legale.
  • Da, l-am lăsat pentru că făcea parte din răspunsul ei, am încercat doar să îl îmbunătățesc, astfel încât să ruleze (pentru cazurile în care nu există spații / linii noi etc. în fișiere).
  • Corecții ale lui – > ei, scuzele mele Jenny: /

Răspuns

I „m grepping un disc de 6 TB pentru a căuta date pierdute și am obținut memoria epuizată – eroare. Acest lucru ar trebui să funcționeze și pentru alte fișiere.

Soluția pe care am venit-o a fost să citim discul în bucăți, folosind dd și să agățăm bucățile. Acesta este codul (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 

Comentarii

  • Dacă nu citești suprapunându-se bucăți, este posibil să pierdeți meciurile de la limitele bucății. Suprapunerea trebuie să fie cel puțin la fel de mare ca șirul pe care vă așteptați să se potrivească.
  • Actualizat pentru a căuta 1 MB în plus în fiecare bucată de 100 MB … hack ieftin

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *