Prováděl jsem velmi jednoduché vyhledávání:

grep -R Milledgeville ~/Documents 

A po nějaké době se objevila tato chyba:

grep: memory exhausted 

Jak se tomu mohu vyhnout?

Mám v systému 10 GB RAM a několik aplikací běží, takže jsem opravdu překvapen, že jednoduchý grep vyčerpá paměť. ~/Documents má přibližně 100 GB a obsahuje všechny druhy souborů.

grep -RI nemusí mít tento problém, ale chci hledat také v binárních souborech.

Odpověď

Dva potenciální problémy:

  • grep -R (kromě upraveného GNU grep nalezeného v OS / X 10.8 a novějších) sleduje symbolické odkazy, takže i když existují pouze 100 GB souborů v ~/Documents, například stále může existovat symbolický odkaz na / a nakonec prohledáte celý systém souborů včetně soubory jako /dev/zero. Použijte grep -r s novější GNU grep nebo použijte standardní syntaxi:

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

    (Všimněte si však, že stav ukončení nebude odrážet skutečnost, že vzor je nebo není shodný.)

  • grep najde řádky, které odpovídají vzoru. K tomu musí načíst jeden řádek po druhém do paměti. GNU grep na rozdíl od mnoha dalších grep implementace nemají omezení velikosti řádků, které čte, a podporují vyhledávání v binárních souborech. Takže pokud máte soubor s velmi velkým řádkem (tj. Se dvěma znaky nového řádku velmi vzdálenými), větším než dostupná paměť, selže.

    To by se obvykle stalo s řídký soubor. Můžete jej reprodukovat pomocí:

    truncate -s200G some-file grep foo some-file 

    Ten je obtížné obejít. Dalo by se to udělat jako (stále s GNU grep):

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

    Tím se převádí sekvence znaků NUL na jeden znak nového řádku před vložením vstupu do grep. To by zahrnovalo případy, kdy je problém způsoben řídkými soubory.

    Můžete jej optimalizovat pouze pro velké soubory:

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

    Pokud soubory nejsou řídké a máte verzi GNU grep před 2.6, můžete použít možnost --mmap. Řádky budou v paměti namapovány na rozdíl od tam zkopírovaných, což znamená, že systém může paměť kdykoli získat zpět y stránkováním stránek do souboru. Tato možnost byla odstraněna v GNU grep 2.6

Komentáře

  • @GodricSeer, stále může číst velkou část souboru do jedné vyrovnávací paměti, ale pokud tam ‚ nenajde řetězec a ‚ Nebyl nalezen ani znak nového řádku, moje sázka spočívá v tom, že tento jediný vyrovnávací paměť uchovává v paměti a čte další vyrovnávací paměť, protože ji bude muset zobrazit, pokud bude nalezena shoda. Problém je tedy stále stejný. V praxi grep na 200GB řídkém souboru selže s OOM.
  • @GodricSeer, no no. Pokud jsou řádky všechny malé, grep může zahodit vyrovnávací paměti, které dosud zpracoval. grep Výstup yes můžete neomezeně používat bez použití více než několika kilobajtů paměti. Problémem je velikost řádků.
  • Možnost GNU grep --null-data zde může být také užitečná. Vynucuje použití NUL namísto nového řádku jako zakončení vstupního řádku.
  • @ 1_CR, dobrý bod, ačkoli to také nastaví zakončení výstupního řádku na NUL.
  • Bylo by složit pomoc s příkazem v těchto situacích? Pomyslete například na dd if=/dev/sda | fold -b $((4096*1024*1024)) | grep -a "some string" , jak omezit množství požadované paměti na 4 GB

Odpovědět

Obvykle ano

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

Vyzkoušel jsem spoustu metod a zjistil jsem, že je to nejrychlejší. Všimněte si, že to nezvládne soubory s mezerami v názvu souboru velmi dobře. Pokud víte, že tomu tak je a máte GNU verzi grep, můžete použít:

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

Pokud ne, můžete použít:

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

Což bude exec grep pro každý soubor.

Komentáře

  • To se u souborů s mezerami rozbije.
  • Hmm, to je pravda.
  • To můžete obejít pomocí find -print0 | xargs -0 grep -ne 'expression'
  • @ChrisDown spíše než neprotekatelného řešení než řešení rozbitého přenosného zařízení.
  • @ChrisDown Most hlavní unices již přijaly find -print0 a xargs -0: všechny tři BSD, MINIX 3, Solaris 11,…

Odpověď

Napadá mě několik způsobů, jak to obejít:

  • Místo toho grepping all files at once, do one file at a time.Příklad:

    find /Documents -type f -exec grep -H Milledgeville "{}" \; 
  • Pokud potřebujete pouze vědět, které soubory obsahují slova, proveďte grep -l místo toho. Vzhledem k tomu, že grep tam přestane hledat po prvním zásahu, nebude muset číst žádné velké soubory.

  • Pokud chcete také vlastní text, můžete řetězec dva oddělte grepy podél:

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

komentářů

  • poslední příklad není platná syntaxe – ‚ d musíte provést substituci příkazu (a neměli byste to ‚ dělat, protože grep výstupy pomocí oddělovače, který je legální v názvech souborů). Musíte také uvést $file.
  • Druhý příklad trpí s problémem názvů souborů, které mají v sobě nový řádek nebo mezery, (způsobí to for zpracování souboru jako dva argumenty)
  • @DravSloan Vaše úpravy, zatímco vylepšení, stále naráží na názvy legálních souborů.
  • Jo, nechal jsem to, protože to bylo součástí její odpovědi, jen jsem se to snažil vylepšit, aby to fungovalo (pro případy, kdy jsem s žádné mezery / nové řádky atd. v souborech).
  • Opravy jeho – > ní, omlouvám se Jenny: /

Odpověď

Zdravím 6TB disk pro hledání ztracených dat a paměť je vyčerpaná – chyba. To by mělo fungovat i pro jiné soubory.

Řešení, které jsme vymysleli, bylo číst disk v blocích pomocí dd a grepovat bloky. Toto je 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 

Komentáře

  • Pokud si nepřečtete překrývající se bloky, možná by vám chyběly zápasy na hranicích bloků. Překrytí musí být minimálně stejně velké jako řetězec, který očekáváte.
  • Aktualizováno, aby prohledalo 1 MB navíc v každém 100 MB bloku … levný hack

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *