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 GNUgrep
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
. Utilizzagrep -r
con la versione più recente di GNUgrep
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. GNUgrep
al contrario di molti altrigrep
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 a2.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 GNUgrep
2.6
Commenti
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
exargs -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
grep
può eliminare i buffer che ha elaborato finora. Puoigrep
loutput diyes
indefinitamente senza utilizzare più di pochi kilobyte di memoria. Il problema è la dimensione delle righe.--null-data
può essere utile qui. Forza luso di NUL invece di newline come terminatore di riga di input.dd if=/dev/sda | fold -b $((4096*1024*1024)) | grep -a "some string"
per limitare la quantità di memoria richiesta a 4 GB