Przeprowadziłem bardzo proste wyszukiwanie:

grep -R Milledgeville ~/Documents 

I po jakimś czasie pojawił się ten błąd:

grep: memory exhausted 

Jak mogę tego uniknąć?

Mam 10 GB pamięci RAM w systemie i kilka aplikacji działa, więc jestem naprawdę zaskoczony, że po prostym grepie zabraknie pamięci. ~/Documents ma około 100 GB i zawiera wszystkie rodzaje plików.

grep -RI może nie mieć tego problemu, ale chcę aby wyszukiwać w plikach binarnych.

Odpowiedź

Dwa potencjalne problemy:

  • grep -R (z wyjątkiem zmodyfikowanego GNU grep znalezionego w systemie OS / X 10.8 i nowszych) podąża za linkami symbolicznymi, więc nawet jeśli są tylko 100 GB plików w ~/Documents, na przykład może nadal istnieć dowiązanie symboliczne do / i ostatecznie przeskanujesz cały system plików, w tym pliki takie jak /dev/zero. Użyj grep -r z nowszym GNU grep lub użyj standardowej składni:

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

    (należy jednak pamiętać, że stan wyjścia nie odzwierciedla faktu, że wzorzec jest dopasowany lub nie).

  • grep znajduje wiersze pasujące do wzorca. W tym celu musi ładować do pamięci po jednym wierszu. GNU grep w przeciwieństwie do wielu innych grep implementacje nie mają ograniczeń co do wielkości linii, które czyta i obsługują wyszukiwanie w plikach binarnych. Tak więc, jeśli masz plik z bardzo dużą linią (to znaczy z dwoma znakami nowej linii bardzo daleko od siebie), większym niż dostępna pamięć, zakończy się niepowodzeniem.

    Zwykle zdarza się to w przypadku rzadki plik. Możesz go odtworzyć za pomocą:

    truncate -s200G some-file grep foo some-file 

    Ten jest trudny do obejścia. Możesz to zrobić jako (nadal z GNU grep):

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

    To konwertuje sekwencje znaków NUL na jeden znak nowej linii przed przekazaniem danych wejściowych do grep. Obejmuje to przypadki, w których problem jest spowodowany rzadkimi plikami.

    Możesz to zoptymalizować, robiąc to tylko dla dużych plików:

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

    Jeśli pliki nie są rzadkie i masz wersję GNU grep przed 2.6, możesz użyć opcji --mmap. Wiersze zostaną zapisane w pamięci zamiast kopiowanych, co oznacza, że system zawsze może odzyskać pamięć y, wysyłając strony do pliku. Ta opcja została usunięta w GNU grep 2.6

Komentarze

  • @GodricSeer, może nadal odczytywać dużą część pliku do pojedynczego bufora, ale jeśli nie ', nie znajdzie tam ciągu i nie ' t również znalazłem znak nowej linii, stawiam na to, że zachowuje ten pojedynczy bufor w pamięci i odczytuje następny bufor, ponieważ będzie musiał go wyświetlić, jeśli zostanie znalezione dopasowanie. Więc problem jest nadal ten sam. W praktyce polecenie grep na pliku rozrzedzonym o rozmiarze 200 GB kończy się niepowodzeniem z OOM.
  • @GodricSeer, no cóż. Jeśli wszystkie linie są małe, grep może odrzucić bufory, które przetworzył do tej pory. Możesz grep wyjście yes w nieskończoność bez użycia więcej niż kilku kilobajtów pamięci. Problemem jest rozmiar linii.
  • Opcja GNU grep --null-data również może być tutaj przydatna. Wymusza użycie NUL zamiast nowej linii jako terminatora linii wejściowej.
  • @ 1_CR, dobra uwaga, chociaż to również ustawia terminator linii wyjściowej na NUL.
  • Czy zwiń polecenie pomocy w takich sytuacjach? Na przykład pomyśl o dd if=/dev/sda | fold -b $((4096*1024*1024)) | grep -a "some string" , aby ograniczyć ilość wymaganej pamięci do 4 GB

Odpowiedź

Zwykle robię

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

Wypróbowałem kilka metod i stwierdziłem, że jest to najszybsza. Zwróć uwagę, że nie obsługuje to plików ze spacjami nazwy pliku bardzo dobrze. Jeśli wiesz, że tak jest i masz wersję grep w wersji GNU, możesz użyć:

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

Jeśli nie, możesz użyć:

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

Co spowoduje exec grep dla każdego pliku.

Komentarze

  • Spowoduje to uszkodzenie plików ze spacjami.
  • Hmm, to prawda.
  • Możesz to obejść za pomocą find -print0 | xargs -0 grep -ne 'expression'
  • @ChrisDown, które jest raczej rozwiązaniem nieprzenośnym niż zepsutym rozwiązaniem przenośnym.
  • @ChrisDown Most główne unices już przyjęły find -print0 i xargs -0: wszystkie trzy BSD, MINIX 3, Solaris 11,…

Odpowiedź

Mogę wymyślić kilka sposobów obejścia tego problemu:

  • Zamiast tego grepowania wszystkich plików naraz, rób jeden plik na raz.Przykład:

    find /Documents -type f -exec grep -H Milledgeville "{}" \; 
  • Jeśli potrzebujesz tylko wiedzieć, które pliki zawierają słowa, zrób grep -l zamiast tego. Ponieważ grep przestanie tam szukać po pierwszym trafieniu, nie będzie musiał czytać żadnych dużych plików

  • Jeśli chcesz również rzeczywisty tekst, możesz napisać dwa oddziel greps wzdłuż:

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

Komentarze

  • Ostatni przykład nie jest poprawną składnią – ' d musisz wykonać podstawienie polecenia (i nie powinieneś ' tego robić, ponieważ grep używa separatora, który jest dozwolony w nazwach plików). Musisz także zacytować $file.
  • Ten ostatni przykład cierpi z problemem nazw plików zawierających znak nowej linii lub spacji (spowoduje to, że for przetworzy plik jako dwa argumenty)
  • @DravSloan Twoja edycja, podczas gdy ulepszenie, wciąż psuje się w legalnych nazwach plików.
  • Tak, zostawiłem to, ponieważ była to część jej odpowiedzi, po prostu próbowałem to poprawić, aby działało (w przypadkach, nie ma spacji / znaków nowej linii itp. w plikach).
  • Poprawki jego – > jej, przepraszam Jenny: /

Odpowiedź

I „m greping 6 TB dysku w celu wyszukania utraconych danych i wyczerpanie pamięci – błąd. To powinno działać również w przypadku innych plików.

Rozwiązaniem, które wymyśliliśmy, było odczytanie dysku w kawałkach za pomocą dd i grepowanie fragmentów. Oto kod (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 

Komentarze

  • Chyba że czytasz nakładające się fragmenty, prawdopodobnie przegapisz dopasowania na granicach fragmentów. Nakładka musi być co najmniej tak duża, jak ciąg, który chcesz dopasować.
  • Zaktualizowano, aby wyszukiwać 1 MB więcej w każdym kawałku 100 MB … tani hack

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *