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 GNUgrep
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žijtegrep -r
s novější GNUgrep
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. GNUgrep
na rozdíl od mnoha dalšíchgrep
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řed2.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 GNUgrep
2.6
Komentáře
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
axargs -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
grep
může zahodit vyrovnávací paměti, které dosud zpracoval.grep
Výstupyes
můžete neomezeně používat bez použití více než několika kilobajtů paměti. Problémem je velikost řádků.--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.dd if=/dev/sda | fold -b $((4096*1024*1024)) | grep -a "some string"
, jak omezit množství požadované paměti na 4 GB