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 modificatgrep
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țigrep -r
cu GNU mai nougrep
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. GNUgrep
spre deosebire de multe altegrep
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 de2.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 GNUgrep
2.6
Comentarii
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
șixargs -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
grep
poate arunca tampoanele pe care le-a procesat până acum. Putețigrep
ieșireayes
la nesfârșit fără a utiliza mai mult de câțiva kiloocteți de memorie. Problema este dimensiunea liniilor.--null-data
poate fi utilă și aici. Forțează utilizarea NUL în loc de linie nouă ca terminator de linie de intrare.dd if=/dev/sda | fold -b $((4096*1024*1024)) | grep -a "some string"
pentru a limita cantitatea de memorie necesară la 4 GB