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 GNU grep 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. Gebruik grep -r met nieuwere GNU grep, 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. GNU grep in tegenstelling tot veel andere grep 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 GNU grep 2.6

Reacties

  • @GodricSeer, kan het nog steeds een groot deel van het bestand in een enkele buffer lezen, maar als het niet ‘ heeft, kan het de tekenreeks daar niet vinden en heeft het geen ‘ t vond ook een newline-teken, mijn weddenschap is dat het die enkele buffer in het geheugen bewaart en de volgende buffer inleest, omdat het deze zal moeten weergeven als er een overeenkomst wordt gevonden. Het probleem is dus nog steeds hetzelfde. In de praktijk mislukt een grep op een 200GB dun bestand met OOM.
  • @GodricSeer, nou nee. Als regels allemaal klein zijn, kan grep de buffers verwijderen die het tot dusver heeft verwerkt. U kunt voor onbepaalde tijd grep de uitvoer van yes gebruiken zonder meer dan een paar kilobytes geheugen te gebruiken. Het probleem is de grootte van de regels.
  • De GNU grep --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.
  • @ 1_CR, goed punt, hoewel dat ook de terminator van de uitvoerlijn op NUL zet.
  • Zou de fold commando hulp in die situaties? Denk bijvoorbeeld aan dd if=/dev/sda | fold -b $((4096*1024*1024)) | grep -a "some string" om de benodigde hoeveelheid geheugen te beperken tot 4GB

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 en xargs -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

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *