Nagyon egyszerű keresést végeztem:

grep -R Milledgeville ~/Documents 

És egy idő után ez a hiba megjelent:

grep: memory exhausted 

Hogyan kerülhetem el ezt?

10 GB RAM van a rendszeremen és kevés alkalmazás van fut, ezért nagyon meglepődtem, hogy egy egyszerű grepnek elfogy a memóriája. A ~/Documents kb. 100 GB, és mindenféle fájlt tartalmaz.

grep -RI lehet, hogy nincs ez a probléma, de szeretném hogy bináris fájlokban is keressen.

Válasz

Két lehetséges probléma:

  • grep -R (kivéve az OS / X 10.8 és újabb verzióin található módosított GNU grep elemeket) szimbolikus linkeket követ, tehát akkor is, ha csak “s” vannak 100 GB fájl a ~/Documents fájlban, előfordulhat, hogy még mindig van szimbolikus hivatkozás például a / fájlra, és végül az egész fájlrendszert átvizsgálja fájlok, például /dev/zero. Használja az grep -r alkalmazást egy újabb GNU-val grep, vagy használja a szokásos szintaxist:

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

    (azonban vegye figyelembe, hogy a kilépési állapot nem tükrözi azt a tényt, hogy a minta illeszkedik-e vagy sem).

  • grep megtalálja a mintának megfelelő vonalakat. Ehhez egyszerre egy sort kell betölteni a memóriába. GNU grep sok más megvalósítások nem korlátozzák az olvasott sorok méretét, és támogatják a bináris fájlokban történő keresést. Tehát, ha van egy fájl, amelynek nagyon nagy sora van (vagyis két újsoros karakter nagyon messze van), nagyobb, mint a rendelkezésre álló memória, akkor meghiúsul.

    Ez általában egy ritka fájl. A következővel reprodukálhatja:

    truncate -s200G some-file grep foo some-file 

    Ezt nehéz megkerülni. Megteheti úgy is (még mindig a GNU grep):

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

    Ez átalakítja a NUL karakterek sorozatát egy új sor karakterré, mielőtt a bemenetet grep. Ez azokra az esetekre vonatkozna, amikor a probléma ritka fájlok miatt következik be.

    Optimalizálhatná úgy, hogy csak nagy fájlok esetén teszi:

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

    Ha a fájlok nem ritkák, és a GNU grep verziója van a , használhatja a --mmap opciót. A sorok a memóriában lesznek leképezve, szemben az ott másolással, ami azt jelenti, hogy a rendszer mindig visszakaphatja a memóriát y az oldalak lapozásával a fájlba. Ezt a lehetőséget eltávolítottuk a GNU grep 2.6

megjegyzésekből

  • @GodricSeer, előfordulhat, hogy a fájl nagy részét egyetlen pufferbe olvassa, de ha nem ‘ nem találja meg a karakterláncot ott, és nincs ‘ t sem talált új sor karaktert, fogadni szeretnék, hogy megtartja ezt az egyetlen puffert a memóriában, és beolvassa a következő puffert, mivel meg kell jelenítenie, ha talál egyezést. Tehát a probléma továbbra is ugyanaz. A gyakorlatban egy 200 GB-os ritka fájl grep-je nem sikerül az OOM-nál.
  • @GodricSeer, nos. Ha az összes vonal kicsi, grep eldobhatja az eddig feldolgozott puffereket. grep a yes kimenetét korlátlanul megadhatja néhány kilobájtnál több memória felhasználása nélkül. A probléma a sorok mérete.
  • A GNU grep --null-data opció itt is hasznos lehet. Kényszeríti a NUL használatát az újsor helyett bemeneti vonal terminátorként.
  • @ 1_CR, jó pont, bár ez a kimeneti vonal terminátort NUL-ra is állítja.
  • A fold parancs segítség ilyen helyzetekben? Gondoljon például a dd if=/dev/sda | fold -b $((4096*1024*1024)) | grep -a "some string" lehetőségre, hogy a szükséges memória mennyiségét 4 GB-ra korlátozza.

Válasz

Általában megteszem

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

Egy csomó módszert kipróbáltam, és ezt találtam a leggyorsabbnak. Ne feledje, hogy ez “nem kezeli nagyon jól a fájlnévvel szóközökkel rendelkező fájlokat. Ha tudja, hogy ez a helyzet, és van egy grn GNU verziója, használhatja:

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

Ha nem, akkor használhatja:

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

Amely exec minden fájlhoz egy grepet fog adni.

Megjegyzések

  • Ez megszakítja a szóközökkel rendelkező fájlokat.
  • Hmm, ez igaz.
  • Ezt megkerülheti a következővel: find -print0 | xargs -0 grep -ne 'expression'
  • @ChrisDown inkább nem védhető megoldás, mint törött hordozható megoldás.
  • @ChrisDown Most a főbb egységek már elfogadták find -print0 és xargs -0: mindhárom BSD, MINIX 3, Solaris 11,…

Válasz

Néhány módot tudok kitalálni:

  • Ehelyett az összes fájl egyszerre történő megcsipegetésével, egyszerre csak egy fájlt végezzen.Példa:

    find /Documents -type f -exec grep -H Milledgeville "{}" \; 
  • Ha csak tudnia kell, hogy mely fájlok tartalmazzák a szavakat, akkor tegye a következőt: grep -l helyett. Mivel a grep az első találat után abbahagyja a keresést, nem kell folytatnia a hatalmas fájlok olvasását.

  • Ha a tényleges szöveget is szeretné, akkor kettőt sztringelhet külön grepek:

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

Megjegyzések

  • Az utolsó példa érvénytelen szintaxis – ‘ parancscserét kell végrehajtania (és ezt nem kell megtennie,

= “cc67858b36”> a fájlnevekben törvényes elválasztó segítségével kimeneti). Idéznie kell a$file-t is.

  • Ez utóbbi példa szenved a fájlnevek kérdésével, amelyekben új vonal vagy szóköz szerepel, (ez a for fájlt két argumentumként fogja feldolgozni)
  • @DravSloan A szerkesztés közben fejlesztés, még mindig megszakítja a jogi fájlneveket.
  • Igen, azért hagytam be, mert része volt a válaszának, csak megpróbáltam javítani, hogy futni tudjon (azokban az esetekben, amikor fájlokban nincs szóköz / újsor stb.).
  • Javításai – > neki, elnézését kérem Jenny: /
  • Válasz

    6 TB-os lemezt vettem az elveszett adatok keresésére, és a memória kimerült -hiba. Ennek más fájloknál is működnie kell.

    A megoldás, amellyel előálltunk, az volt, hogy a lemezt darabokban olvastuk el a dd használatával, és megcsipkedtük a darabokat. Ez a kód (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 

    Megjegyzések

    • Hacsak nem olvasol átfedő darabok, valószínűleg hiányoznának a meccsek a darabok határán. Az átfedésnek legalább akkorának kell lennie, mint az a karakterlánc, amelyre számítania szeretne.
    • Frissítve 1 MB-os extra keresésre minden 100 MB-os darabban … olcsó hack

    Vélemény, hozzászólás?

    Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük