Jag gjorde en mycket enkel sökning:
grep -R Milledgeville ~/Documents
Och efter en tid uppstod detta fel:
grep: memory exhausted
Hur kan jag undvika detta?
Jag har 10 GB RAM på mitt system och få applikationer kör, så jag är verkligen förvånad över att en enkel grep tar slut på minne. ~/Documents
är ungefär 100 GB och innehåller alla typer av filer.
grep -RI
kanske inte har det här problemet, men jag vill att söka i binära filer också.
Svar
Två potentiella problem:
-
grep -R
(förutom den modifierade GNUgrep
som finns på OS / X 10.8 och senare) följer symlänkar, så även om det bara finns 100 GB filer i~/Documents
, det kan fortfarande finnas en symlänk till/
till exempel och du kommer att sluta skanna hela filsystemet inklusive filer som/dev/zero
. Användgrep -r
med nyare GNUgrep
, eller använd standardsyntaxen:find ~/Documents -type f -exec grep Milledgeville /dev/null {} +
(dock observera att utgångsstatusen inte återspeglar det faktum att mönstret matchas eller inte).
-
grep
hittar raderna som matchar mönstret. För det måste den ladda en rad i taget i minnet. GNUgrep
i motsats till många andragrep
-implementeringar har inte en gräns för storleken på raderna som den läser och stöder sökning i binära filer. Så om du har en fil med en mycket stor rad (det vill säga med två nylinjetecken mycket långt), större än tillgängligt minne, kommer den att misslyckas.Det skulle vanligtvis hända med en gles fil. Du kan reproducera den med:
truncate -s200G some-file grep foo some-file
Den här är svår att komma runt. Du kan göra det som (fortfarande 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 omvandlar sekvenser av NUL-tecken till ett nytt linjetecken innan ingången matas till
grep
. Det skulle täcka för fall där problemet beror på glesa filer.Du kan optimera det genom att bara göra det för stora 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 {} + \)
Om filerna är inte glesa och du har en version av GNU
grep
före2.6
, du kan använda alternativet--mmap
. Raderna mappas i minnet i motsats till kopieras där, vilket innebär att systemet alltid kan återta minnet y genom att söka ut sidorna till filen. Det alternativet togs bort i GNUgrep
2.6
Kommentarer
- @GodricSeer kan det fortfarande läsa en stor del av filen i en enda buffert, men om den inte ’ inte hittar strängen där och inte har ’ Hittade inte heller en nylinjetecken, min insats är att den håller den enskilda bufferten i minnet och läser nästa buffert i, eftersom den måste visa den om en matchning hittas. Så problemet är fortfarande detsamma. I praktiken misslyckas en grep på en 200 GB sparsam fil med OOM.
- @GodricSeer, ja nej. Om raderna alla är små kan
grep
kasta buffertarna som den har bearbetat hittills. Du kangrep
utdata frånyes
på obestämd tid utan att använda mer än några kilobyte minne. Problemet är storleken på raderna. - Alternativet GNU grep
--null-data
kan också vara användbart här. Det tvingar användningen av NUL istället för newline som en ingångsledningsavslutare. - @ 1_CR, bra punkt, men det sätter också utgångsledningsavslutaren till NUL.
- Skulle fold kommandohjälp i dessa situationer? Tänk till exempel på
dd if=/dev/sda | fold -b $((4096*1024*1024)) | grep -a "some string"
för att begränsa mängden minne som krävs till 4 GB
Svar
Jag brukar göra
find ~/Documents | xargs grep -ne "expression"
Jag försökte en massa metoder och fann att det här var det snabbaste. Observera att detta inte hanterar filer med mellanslag filnamnet mycket bra. Om du vet att detta är fallet och har en GNU-version av grep kan du använda:
find ~/Documents -print0 | xargs -0 grep -ne "expression"
Om inte kan du använda:
find ~/Documents -exec grep -ne "expression" "{}" \;
Vilket kommer exec
en grep för varje fil.
Kommentarer
Svar
Jag kan tänka mig några sätt att komma runt detta:
-
Istället för att greppa alla filer samtidigt, gör en fil i taget.Exempel:
find /Documents -type f -exec grep -H Milledgeville "{}" \;
-
Om du bara behöver veta vilka filer som innehåller orden, gör
grep -l
istället. Eftersom grep slutar söka efter den första träffen, behöver den inte fortsätta läsa några enorma filer -
Om du också vill ha den faktiska texten kan du stränga två separata greps längs:
for file in $( grep -Rl Milledgeville /Documents ); do grep -H Milledgeville "$file"; done
Kommentarer
- Det sista exemplet är inte giltig syntax – du ’ d behöver utföra ett kommandosubstitution (och du bör inte ’ t göra det, eftersom
grep
utdata med en avgränsare som är laglig i filnamn). Du måste också citera$file
. - Det senare exemplet lider med frågan om filnamn som innehåller ny linje eller tomt utrymme, (det kommer att göra att
for
bearbetar filen som två argument) - @DravSloan Din redigering, medan en förbättring, bryter fortfarande med lagliga filnamn.
- Ja, jag lämnade det eftersom det var en del av hennes svar, jag försökte bara förbättra det så att det skulle gå (för de fall där jag s inga mellanslag / nya rader etc i filer).
- Korrigeringar av hans – > henne, min ursäkt Jenny: /
Svar
Jag tar en 6TB-disk för att söka efter förlorade data och fick minnet uttömt -fel. Detta bör också fungera för andra filer.
Lösningen vi kom fram till var att läsa disken i bitar genom att använda dd och greppa bitarna. Det här är 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
- Om du inte läser överlappande bitar, du skulle eventuellt missa matcher på bitgränserna. Överlappningen måste vara minst lika stor som strängen som du förväntar dig att matcha.
- Uppdaterad för att söka 1 MB extra i varje 100 MB bit … billigt hack
find -print0 | xargs -0 grep -ne 'expression'
find -print0
ochxargs -0
nu: alla tre BSD, MINIX 3, Solaris 11,…