grep: hafıza bitti


42

Çok basit bir arama yapıyordum:

grep -R Milledgeville ~/Documents

Ve bir süre sonra bu hata ortaya çıktı:

grep: memory exhausted

Bundan nasıl kaçınabilirim?

Sistemimde 10GB RAM var ve çalışan birkaç uygulama var, bu yüzden hafızamızda basit bir iz bıraktığına şaşırdım. ~/Documentsyaklaşık 100GB'tır ve her türlü dosyayı içerir.

grep -RI Bu problemi olmayabilir, ama ben de ikili dosyalarda aramak istiyorum.

Yanıtlar:


46

İki potansiyel problem:

  • grep -R( grepOS / X 10.8 ve üstünde bulunan değiştirilmiş GNU hariç ) sembolik bağlantıları izler, böylece sadece 100GB'lık dosyalar olsa bile ~/Documents, /örneğin bir sembolik bağlantı olabilir ve dosyalar dahil tüm dosya sistemini taramaya başlayabilirsiniz. gibi /dev/zero. Daha grep -ryeni GNU ile kullanın grepveya standart sözdizimini kullanın:

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

    (ancak çıkış durumunun, kalıbın eşleşip eşleşmediğini yansıtmayacağını unutmayın).

  • grepdesenle eşleşen çizgileri bulur. Bunun için, hafızada bir seferde bir satır yüklemek zorundadır. grepDiğer birçok grepuygulamaya zıt olarak GNU , okuduğu satırların boyutuna sınır getirmez ve ikili dosyalarda aramayı destekler. Bu nedenle, çok büyük bir satırda (yani, çok uzaktaki iki yeni satır karakteriyle) bir dosya varsa, kullanılabilir bellekten daha büyükse, başarısız olur.

    Bu genellikle seyrek bir dosya ile olur. Şununla çoğaltabilirsiniz:

    truncate -s200G some-file
    grep foo some-file
    

    Bunu çözmek zor. Yapabilirsin (hala GNU ile grep):

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

    Bu, girişi beslemeden önce NUL karakter dizilerini bir yeni satır karakterine dönüştürür grep. Sorunun seyrek dosyalardan kaynaklandığı durumları kapsar.

    Yalnızca büyük dosyalar için yaparak optimize edebilirsiniz:

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

    Dosyalar seyrek değilse ve grepöncesinde bir GNU sürümü 2.6varsa, --mmapseçeneği kullanabilirsiniz . Satırlar orada kopyalandığından ziyade belleğe eşleştirilecektir, bu da sistemin her zaman sayfaları sayfalara ayırarak belleği geri alabileceği anlamına gelir. Bu seçenek GNU grep2.6’da kaldırıldı


Aslında, GNU grep 1 satırda okumayı umursamıyor, dosyanın büyük bir bölümünü tek bir tamponda okuyor. "Dahası, GNU grep avluyor HATLARIN GİRİŞİNİ KIRIYOR." kaynak: lists.freebsd.org/pipermail/freebsd-current/2010-Ağustos/…
Godric Seer

4
@GodricSeer, hala dosyanın büyük bir bölümünü tek bir arabellekte okuyabilir, ancak oradaki dizgiyi bulamadıysa ve yeni bir satır karakteri bulamazsa, benim iddiam bu tek ara belleği bellekte tutmasıdır. ve bir eşleşme bulunursa göstermesi gerekeceğinden sonraki tamponu okur. Yani sorun hala aynı. Uygulamada, 200GB'lık seyrek bir dosyadaki grep OOM ile başarısız olur.
Stéphane Chazelas 10:13

1
@GodricSeer, iyi değil. Çizgilerin tümü küçükse, grepşu ana kadar işlediği arabellekleri atabilir. Birkaç kilobayttan fazla bellek kullanmadan süresiz olarak grepçıktı alabilirsiniz yes. Sorun , çizgilerin boyutudur.
Stéphane Chazelas 10:13

3
GNU grep --null-dataseçeneği burada da yararlı olabilir. Bir giriş satırı sonlandırıcı olarak newline yerine NUL kullanımını zorlar.
iruvar

1
@ 1_CR, iyi nokta, ancak çıkış satırı sonlandırıcısını NUL olarak da ayarlıyor.
Stéphane Chazelas 16:13

5

genellikle yaparım

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

Bir sürü yöntem denedim ve bunu en hızlı buldum. Bunun boşluk içeren dosyaları çok iyi işlemeyeceğini unutmayın. Durumun bu olduğunu ve grep'in GNU sürümüne sahip olduğunu biliyorsanız, kullanabilirsiniz:

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

Değilse kullanabilirsiniz:

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

execHer dosya için bir grep olacaktır .


Bu boşluk içeren dosyalara zarar verir.
Chris Down,

Hmm, bu doğru.
Kotte

Bu konuda alabilirsinizfind -print0 | xargs -0 grep -ne 'expression'
Drav Sloan

@ChrisDown, kırılabilir portatif bir çözümden ziyade protable olmayan bir çözüm.
Reto

@ChrisDown Çoğu büyük Unix sistemleri benimsediler find -print0ve xargs -0artık: Her üç BSD MINIX 3, Solaris 11, ...
Gilles 'SO dur olma kötülüğü'

4

Bunu aşmanın birkaç yolunu düşünebilirim:

  • Tüm dosyaları aynı anda okumak yerine, bir kerede bir dosya yapın. Örnek:

    find /Documents -type f -exec grep -H Milledgeville "{}" \;
    
  • Yalnızca kelimeleri içeren dosyaları bilmeniz gerekiyorsa, grep -lbunun yerine yapın. Grep orada ilk vuruştan sonra aramayı durduracağından büyük dosyaları okumaya devam etmek zorunda kalmayacak

  • Asıl metni de isterseniz, iki ayrı sıyrık boyunca dizebilirsiniz:

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

Son örnek geçerli bir sözdizimi değil - bir komut değişikliği yapmanız gerekecek ( grepdosya adında yasal bir sınırlayıcı kullanarak çıktığı için bunu yapmamalısınız ). Ayrıca teklif etmeniz gerekiyor $file.
Chris Down,

İkinci örnek, içinde yeni satır veya boşluk bulunan dosya isimleri sorunuyla karşı for
karşıyadır

@DravSloan Düzenlemeniz sırasında bir iyileştirme olsa da hala yasal dosya adlarını ihlal ediyor.
Chris Down,

1
Evet, cevabımın bir parçası olduğu için bıraktım, çalışmasını sağlamak için geliştirmeye çalıştım (dosyalarda boşluk / yeni satır vb. Olmadığı durumlarda).
Drav Sloan

Onun düzeltmeleri -> onu, özür dilerim Jenny: /
Drav Sloan

1

Kayıp verileri aramak için 6TB'lik bir diski grepliyorum ve hafızamı yordum - korku. Bu diğer dosyalar için de işe yaramalı.

Karşılaştığımız çözüm, diski topaklarda dd kullanarak okumak ve topakları greplemekti. Bu 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

1
Örtüşen öbekleri okumadığınız sürece , öbek sınırları üzerindeki eşleşmeleri muhtemelen kaçırırsınız. Örtüşme en azından eşleşmeyi beklediğiniz dizge kadar büyük olmalıdır.
Kusalananda

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.