Stavo facendo una ricerca molto semplice:

grep -R Milledgeville ~/Documents 

E dopo un po di tempo è apparso questo errore:

grep: memory exhausted 

Come posso evitarlo?

Ho 10 GB di RAM sul mio sistema e poche applicazioni in esecuzione, quindi sono davvero sorpreso che un semplice grep esaurisca la memoria. ~/Documents è di circa 100 GB e contiene tutti i tipi di file.

grep -RI potrebbe non avere questo problema, ma vorrei per cercare anche nei file binari.

Risposta

Due potenziali problemi:

  • grep -R (eccetto GNU grep modificato trovato su OS / X 10.8 e versioni successive) segue i collegamenti simbolici, quindi anche se sono presenti solo 100 GB di file in ~/Documents, potrebbe essere ancora presente un collegamento simbolico a /, ad esempio, e finirai per scansionare lintero file system, incluso file come /dev/zero. Utilizza grep -r con la versione più recente di GNU grep oppure utilizza la sintassi standard:

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

    (tuttavia, tieni presente che lo stato di uscita non riflette il fatto che il pattern sia abbinato o meno).

  • grep trova le righe che corrispondono al pattern. Per questo, deve caricare una riga alla volta in memoria. GNU grep al contrario di molti altri grep le implementazioni non hanno limiti alla dimensione delle righe che legge e supporta la ricerca nei file binari. Quindi, se hai un file con una linea molto grande (cioè, con due caratteri di nuova riga molto distanti), più grande della memoria disponibile, fallirà.

    Ciò accadrebbe tipicamente con un file sparse. Puoi riprodurlo con:

    truncate -s200G some-file grep foo some-file 

    Questo è difficile da aggirare. Puoi farlo come (sempre con GNU grep):

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

    che converte le sequenze di caratteri NUL in un carattere di nuova riga prima di fornire linput a grep. Questo coprirebbe i casi in cui il problema è dovuto a file sparsi.

    Puoi ottimizzarlo facendolo solo per file di grandi dimensioni:

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

    Se i file non sono sparsi e disponi di una versione di GNU grep precedente a 2.6, puoi utilizzare lopzione --mmap. Le linee verranno mappate in memoria invece di essere copiate lì, il che significa che il sistema può sempre recuperare la memoria y sfogliando le pagine nel file. Questa opzione è stata rimossa in GNU grep 2.6

Commenti

  • @GodricSeer, può ancora leggere una gran parte del file in un unico buffer, ma se ‘ non trova la stringa e non ‘ Neanche un carattere di nuova riga è stato trovato, la mia scommessa è che mantiene quel singolo buffer in memoria e legge il buffer successivo, poiché dovrà visualizzarlo se viene trovata una corrispondenza. Quindi, il problema è sempre lo stesso. In pratica, un grep su un file sparse da 200 GB non riesce con OOM.
  • @GodricSeer, beh no. Se le righe sono tutte piccole, grep può eliminare i buffer che ha elaborato finora. Puoi grep loutput di yes indefinitamente senza utilizzare più di pochi kilobyte di memoria. Il problema è la dimensione delle righe.
  • Anche lopzione GNU grep --null-data può essere utile qui. Forza luso di NUL invece di newline come terminatore di riga di input.
  • @ 1_CR, buon punto, anche se questo imposta anche il terminatore di riga di output su NUL.
  • Il fold aiuto al comando in quelle situazioni? Ad esempio, pensa a dd if=/dev/sda | fold -b $((4096*1024*1024)) | grep -a "some string" per limitare la quantità di memoria richiesta a 4 GB

Risposta

Di solito lo faccio

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

Ho provato un sacco di metodi e ho trovato che questo è il più veloce. Nota che questo non gestisce molto bene i file con spazi il nome del file. Se sai che è così e hai una versione GNU di grep, puoi usare:

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

In caso contrario, puoi utilizzare:

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

che exec un grep per ogni file.

Commenti

  • Questo interromperà i file con spazi.
  • Hmm, questo è vero.
  • Puoi aggirare il problema con find -print0 | xargs -0 grep -ne 'expression'
  • @ChrisDown piuttosto una soluzione non protettiva che non portatile.
  • @ChrisDown Most le principali università hanno adottato find -print0 e xargs -0: tutti e tre BSD, MINIX 3, Solaris 11,…

Risposta

Posso pensare ad alcuni modi per aggirare questo problema:

  • Invece di grepping tutti i file in una volta, fare un file alla volta.Esempio:

    find /Documents -type f -exec grep -H Milledgeville "{}" \; 
  • Se hai solo bisogno di sapere quali file contengono le parole, fai grep -l invece. Dato che grep interromperà la ricerca dopo il primo colpo, non dovrà continuare a leggere file enormi

  • Se vuoi anche il testo effettivo, puoi stringere due greps separati:

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

Commenti

  • Lultimo esempio non è una sintassi valida – ‘ devi eseguire una sostituzione del comando (e non dovresti ‘ farlo, poiché grep restituisce utilizzando un delimitatore che è legale nei nomi dei file). Devi anche citare $file.
  • Lultimo esempio soffre con il problema di nomi di file che contengono una nuova riga o uno spazio bianco, (farà sì che for elabori il file come due argomenti)
  • @DravSloan La tua modifica, mentre un miglioramento, ancora interrompe i nomi di file legali.
  • Sì, lho lasciato perché faceva parte della sua risposta, ho solo provato a migliorarlo in modo che funzionasse (per i casi in cui ho s senza spazi / nuove righe ecc. nei file).
  • Correzioni di – > lei, mi scuso Jenny: /

Risposta

Sto “grepping” un disco da 6 TB per cercare i dati persi e ho esaurito la memoria -error. Questo dovrebbe funzionare anche per altri file.

La soluzione che abbiamo trovato è stata di leggere il disco in blocchi utilizzando dd e grepping dei blocchi. Questo è il codice (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 

Commenti

  • A meno che tu non legga sovrapposizione di blocchi, potresti perdere le corrispondenze sui limiti dei blocchi. La sovrapposizione deve essere grande almeno quanto la stringa che ci si aspetta di trovare.
  • Aggiornato per cercare 1 MB in più in ogni blocco da 100 MB … hack economico

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *