Ik deed een heel eenvoudige zoekopdracht:
grep -R Milledgeville ~/Documents
En na enige tijd verscheen deze fout:
grep: memory exhausted
Hoe kan ik dit voorkomen?
Ik heb 10 GB RAM op mijn systeem en enkele applicaties loopt, dus ik ben echt verrast dat een simpele grep geen geheugen meer heeft. ~/Documents
is ongeveer 100GB en bevat allerlei soorten bestanden.
grep -RI
heeft dit probleem misschien niet, maar ik wil om ook in binaire bestanden te zoeken.
Antwoord
Twee mogelijke problemen:
-
grep -R
(behalve de gewijzigde GNUgrep
gevonden op OS / X 10.8 en hoger) volgt symlinks, dus zelfs als er alleen 100 GB aan bestanden in~/Documents
, kan er nog steeds een symlink zijn naar bijvoorbeeld/
en u zult uiteindelijk het hele bestandssysteem scannen, inclusief bestanden zoals/dev/zero
. Gebruikgrep -r
met nieuwere GNUgrep
, of gebruik de standaard syntaxis:find ~/Documents -type f -exec grep Milledgeville /dev/null {} +
(houd er echter rekening mee dat de exit-status “niet het feit weergeeft dat het patroon overeenkomt of niet).
-
grep
vindt de regels die overeenkomen met het patroon. Daarvoor moet het één regel tegelijk in het geheugen laden. GNUgrep
in tegenstelling tot veel anderegrep
implementaties hebben geen limiet aan de grootte van de regels die het leest en ondersteunen zoeken in binaire bestanden. Dus als je een bestand hebt met een hele grote regel (dat wil zeggen, met twee nieuwe-regeltekens die ver van elkaar verwijderd zijn), groter dan het beschikbare geheugen, zal het mislukken.Dat zou normaal gesproken gebeuren met een sparse bestand. Je kunt het reproduceren met:
truncate -s200G some-file grep foo some-file
Dat is moeilijk te omzeilen. Je zou het kunnen doen als (nog steeds met GNU
grep
):find ~/Documents -type f -exec sh -c "for i do tr -s "\0" "\n" < "$i" | grep --label="$i" -He "$0" done" Milledgeville {} +
Dat zet reeksen NUL-tekens om in één teken voor een nieuwe regel voordat de invoer wordt ingevoerd naar
grep
. Dat dekt gevallen waarin het probleem te wijten is aan schaarse bestanden.Je zou het kunnen optimaliseren door het alleen te doen voor grote bestanden:
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 {} + \)
Als de bestanden niet schaars zijn en u een versie van GNU
grep
vóór , kunt u de optie--mmap
gebruiken. De regels worden in het geheugen gemapt in plaats van daarheen gekopieerd, wat betekent dat het systeem altijd het geheugen kan terughalen y door de paginas naar het bestand te bladeren. Die optie is verwijderd in GNUgrep
2.6
Reacties
Answer
Meestal doe ik dat
find ~/Documents | xargs grep -ne "expression"
Ik heb een aantal methoden geprobeerd en vond dit de snelste. Merk op dat dit “bestanden met spaties niet goed verwerkt, de bestandsnaam. Als je weet dat dit het geval is en je hebt een GNU-versie van grep, dan kun je gebruiken:
find ~/Documents -print0 | xargs -0 grep -ne "expression"
Zo niet, dan kunt u gebruiken:
find ~/Documents -exec grep -ne "expression" "{}" \;
Wat exec
een grep zal zijn voor elk bestand.
Opmerkingen
- Dit zal breken op bestanden met spaties.
- Hmm, dat is waar.
- Je kunt dat omzeilen met
find -print0 | xargs -0 grep -ne 'expression'
- @ChrisDown is eerder een niet-draagbare oplossing dan een kapotte draagbare oplossing.
- @ChrisDown Meest grote unices hebben inmiddels
find -print0
enxargs -0
geadopteerd: alle drie BSD, MINIX 3, Solaris 11, …
Antwoord
Ik kan een paar manieren bedenken om dit te omzeilen:
-
In plaats daarvan om alle bestanden tegelijk te grepen, moet u één bestand tegelijk maken.Voorbeeld:
find /Documents -type f -exec grep -H Milledgeville "{}" \;
-
Als je alleen wilt weten welke bestanden de woorden bevatten, doe dan
grep -l
in plaats daarvan. Aangezien grep daar stopt met zoeken na de eerste treffer, hoeft het “geen grote bestanden te blijven lezen. -
Als je de eigenlijke tekst ook wilt, kun je er twee aparte greps langs:
for file in $( grep -Rl Milledgeville /Documents ); do grep -H Milledgeville "$file"; done
Reacties
- Het laatste voorbeeld is geen geldige syntaxis – u ‘ moet een opdrachtvervanging uitvoeren (en u moet ‘ dat niet doen, aangezien
grep
voert uit met een scheidingsteken dat legaal is in bestandsnamen). Je moet ook$file
citeren. - Het laatste voorbeeld lijdt met het probleem van bestandsnamen met een nieuwe regel of witruimte, (het zorgt ervoor dat
for
het bestand als twee argumenten verwerkt) - @DravSloan Uw bewerking, terwijl een verbetering, maar breekt nog steeds met legale bestandsnamen.
- Ja, ik liet het staan omdat het deel uitmaakte van haar antwoord, ik probeerde het gewoon te verbeteren zodat het zou werken (voor de gevallen waarin ik s geen spaties / nieuwe regels enz. in bestanden).
- Correcties van zijn – > haar, mijn excuses Jenny: /
Answer
Ik “grijp een 6TB-schijf om naar verloren gegevens te zoeken, en heb het geheugen uitgeput -fout. Dit zou ook voor andere bestanden moeten werken.
De oplossing die we bedachten was om de schijf in stukjes te lezen door dd te gebruiken en de stukjes te grepen. Dit is de code (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
Reacties
- Tenzij je leest overlappende chunks, zou je mogelijk matches missen op de chunkgrenzen. De overlap moet minstens zo groot zijn als de string die u verwacht te matchen.
- Bijgewerkt om 1 MB extra te zoeken in elk stuk van 100 MB … goedkope hack
grep
de buffers verwijderen die het tot dusver heeft verwerkt. U kunt voor onbepaalde tijdgrep
de uitvoer vanyes
gebruiken zonder meer dan een paar kilobytes geheugen te gebruiken. Het probleem is de grootte van de regels.--null-data
optie kan hier ook nuttig zijn. Het dwingt het gebruik van NUL in plaats van newline af als een terminator voor de invoerregel.dd if=/dev/sda | fold -b $((4096*1024*1024)) | grep -a "some string"
om de benodigde hoeveelheid geheugen te beperken tot 4GB