Özyinelemeli grep vs / /type f -exec grep bulma {} \; Hangisi daha verimli / daha hızlı?


70

Tüm dosya sistemindeki hangi dosyaların bir dize içerdiğini bulmak için hangisi daha etkilidir: recursive grep veya grep ile bir exec ifadesinde bulmak? Dosya uzantısını veya dosya adıyla eşleşen bir regex'i biliyorsanız en azından biraz filtreleme yapabildiğiniz için bulmanın daha etkili olacağını varsayarım, ancak -type fhangisinin daha iyi olduğunu biliyor musunuz ? GNU grep 2.6.3; Bul (GNU bulguları) 4.4.2

Örnek:

grep -r -i 'the brown dog' /

find / -type f -exec grep -i 'the brown dog' {} \;


1
Matematik / bilgisayar bilimi / algoritma verimliliği görüşe dayalı değildir.
Gregg Leventhal,

Bunu kontrol et. Özyinelemeli olmasa da, hangisinin daha iyi olduğu konusunda bir anlayış verecektir. unix.stackexchange.com/questions/47983/…
Ramesh

8
@AvinashRaj o fikir istemiyor. Hangisinin "daha iyi" değil hangisinin daha verimli ve / veya daha hızlı olduğunu soruyor . Bu, bu iki programın işlerini nasıl yaptıklarına ve arama yapmak için tam olarak neye verdiğinize bağlı olan tek ve özel bir cevabı olan mükemmel cevaplanabilir bir sorudur.
terdon

2
-exec {} +Formun daha az çatal yapacağına dikkat edin , bu nedenle daha hızlı olması gerekir -exec {} \;. Tam olarak eşdeğer çıktı elde etmek için seçeneklere eklemeniz -H(veya -h) gerekebilir grep.
Mikel

Muhtemelen ikinci -rseçenek için seçeneği istemiyordunuzgrep
qwertzguy

Yanıtlar:


85

Emin değilim:

grep -r -i 'the brown dog' /*

Gerçekten demek istediğin. Bu, gizli olmayan tüm dosyalarda ve dizinlerde özyinelemeli grep anlamına gelir /(ama yine de içindeki gizli dosyalara ve dizinlere bakın).

Demek istediğini varsayalım:

grep -r -i 'the brown dog' /

Unutulmaması gereken birkaç şey:

  • Tüm grepuygulamalar desteklemiyor -r. Ve bunu yapanlar arasında davranışlar farklıdır: bazıları dizin ağacını geçerken dizinlere sembolik bağlantılar izler (bu, aynı dosyada birkaç kez bakmanız veya hatta sonsuz döngülerde çalıştırmanız anlamına gelir), bazıları olmaz. Bazıları aygıt dosyalarına bakacak (ve /dev/zeroörneğin oldukça zaman alacaktır ) veya borular ya da ikili dosyalar ...
  • Gibi verimlidir grepkısa sürede onları keşfeder olarak içindeki dosyaları aramaya başlar. Ancak bir dosyaya bakarken, aranacak başka dosya aramıyor (çoğu durumda olduğu gibi muhtemelen)

Sizin:

find / -type f -exec grep -i 'the brown dog' {} \;

( -rburada hangi anlam ifade etmedi kaldırıldı ) korkunç verimsiz çünkü grepdosya başına bir tane çalışıyoruz . ;sadece bir argüman kabul eden komutlar için kullanılmalıdır. Üstelik burada, grepsadece bir dosyaya bakıldığı için dosya adını yazmaz, böylece eşleşmelerin nerede olduğunu bilemezsiniz.

Cihaz dosyalarına, borulara, sembolik bağlantılara bakmıyorsunuz ..., sembolik bağlantılara uymuyorsunuz, ama yine de potansiyel olarak içindeki şeylere bakıyorsunuz /proc/mem.

find / -type f -exec grep -i 'the brown dog' {} +

Çok daha iyi olurdu çünkü grepmümkün olduğu kadar az komut çalıştırılacaktı. Son çalıştırmada yalnızca bir dosya yoksa, dosya adını alırsınız. Bunun için kullanmak daha iyidir:

find / -type f -exec grep -i 'the brown dog' /dev/null {} +

veya GNU ile grep:

find / -type f -exec grep -Hi 'the brown dog' {} +

Çiğnemek için yeterli dosya grepbulana kadar başlatılmayacağını unutmayın find, bu nedenle başlangıçta bir gecikme olacaktır. Ve bir finddaha grepgeri dönene kadar daha fazla dosya aramaya devam etmeyecek . Büyük dosya listesinin tahsis edilmesi ve aktarılması bazı (muhtemelen ihmal edilebilir) bir etkiye sahiptir, bu yüzden hepsinde, muhtemelen grep -rlinke uymayan veya cihazların içine bakmayan birinden daha az verimli olacaktır .

GNU araçlarıyla:

find / -type f -print0 | xargs -r0 grep -Hi 'the brown dog'

Yukarıdaki gibi, grepmümkün olduğu kadar az örnek çalıştırılacak, ancak findilk grepçağrı ilk toplu işin içine bakarken daha fazla dosya aramaya devam edecek . Bu olsa da bir avantaj olabilir veya olmayabilir. Örneğin, dönme sabit diskler üzerinde depolanan verilerin, ile findve grepdiskte farklı konumlarda depolanan erişen verileri sürekli hareket etmesine disk kafası neden olarak, disk verim yavaşlayacaktır. Bir RAID kurulumunda ( farklı disklere erişebilir findve greperişebilir) veya SSD'lerde, bu olumlu bir fark yaratabilir.

Bir RAID kurulumunda, birkaç eşzamanlı grep çağrı yürütmek de işleri iyileştirebilir. Hala 3 diskli RAID1 depolama alanındaki GNU araçlarıyla,

find / -type f -print0 | xargs -r0 -P2 grep -Hi 'the brown dog'

performansı önemli ölçüde artırabilir. Ancak ikincisinin grepyalnızca ilk grepkomutu doldurmaya yetecek kadar dosya bulunduğunda başlatılacağını unutmayın . Bunun daha erken gerçekleşmesi için bir -nseçenek ekleyebilirsiniz xargs(ve grepçağrı başına daha az dosya iletir).

Ayrıca, xargsçıkışı bir terminal cihazından başka bir şeye yönlendiriyorsanız , grepss çıkışlarını tamponlamaya başlayacaktır; bu, bunların çıkışının grepmuhtemelen yanlış bir şekilde birleştirileceği anlamına gelir . stdbuf -oLEtrafında çalışmak için (GNU veya FreeBSD'de olduğu gibi) kullanmanız gerekecek (hala çok uzun çizgilerle (tipik olarak> 4KiB) problemleriniz olabilir) veya her birinin çıktılarını ayrı bir dosyaya yazıp birleştirme yapmalısınız. hepsi sonunda.

Burada, aradığınız dize sabittir (bir regexp değil), bu nedenle -Fseçeneği kullanmak bir fark yaratabilir ( grepuygulamaların zaten bunu nasıl optimize edeceğini bilmesi olası değildir ).

Büyük bir fark yaratabilecek başka bir şey, çok baytlık bir yerel ayardaysanız yerel ayarı C'ye sabitlemektir:

find / -type f -print0 | LC_ALL=C xargs -r0 -P2 grep -Hi 'the brown dog'

İçine bakarak önlemek için /proc, /sys..., kullanmak -xdeviçeri aramak istediğiniz dosya sistemleri ve belirtin:

LC_ALL=C find / /home -xdev -type f -exec grep -i 'the brown dog' /dev/null {} +

Ya da açıkça dışlamak istediğiniz yolları bulayın:

LC_ALL=C find / \( -path /dev -o -path /proc -o -path /sys \) -prune -o \
  -type f -exec grep -i 'the brown dog' /dev/null {} +

Birinin beni bir kaynağa yöneltebileceğini sanmıyorum - ya da açıkla - {} ve + ne anlama geliyor? Man sayfalarında exec, grep veya kullandığım Solaris kutusunda bulabileceğim hiçbir şey yok. Sadece kabuk dosya isimlerini birleştirip onları grep'e aktarmak mı?

3
@Poldie, Solaris man sayfasındaki-exec yüklemin açıklamasında açıkça açıklanmıştır
Stéphane Chazelas

Ah evet. Adam sayfasında arama yaparken {karakterimden kaçmadım. Bağlantınız daha iyi; Adam sayfalarını okumak için korkunç buluyorum.

1
RAID1 w / 3 diskleri? Ne garip ...
tink

1
Bakın, evet RAID1 2 veya daha fazla diskte. 2 diske kıyasla 3 diskle, fazlalığı artırır ve yazma performansını kabaca aynı tutarken performansı okuyun. 2'ye karşılık 3 diskle, bu da hataları düzeltebilirsiniz, bir bit kopyalardan birinin üzerine geldiğinde, 2 diskin olduğu sırada 3 kopyayı da kontrol ederek hangisinin doğru olduğunu söyleyebilirsiniz. gerçekten söyle.
Stéphane Chazelas

13

Eğer *içinde grepçağrı sizin için önemli değildir, sonra ilk sadece bir örneği olarak daha verimli olmalıdır grepbaşlatılır ve çatal serbest değildir. Çoğu durumda *sıralama daha hızlı olur, ancak son durumlarda sıralama bunu tersine çevirebilir.

Başka olabilir find- grepözellikle birçok küçük dosyada daha iyi çalışan yapılar. Büyük miktarda dosya girişi ve inode'u bir kerede okumak, döndürülen ortamlarda performans iyileştirmesi sağlayabilir.

Fakat sistem çağrı istatistiklerine bakalım:

bulmak

> strace -cf find . -type f -exec grep -i -r 'the brown dog' {} \;
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 97.86    0.883000        3619       244           wait4
  0.53    0.004809           1      9318      4658 open
  0.46    0.004165           1      6875           mmap
  0.28    0.002555           3       977       732 execve
  0.19    0.001677           2       980       735 stat
  0.15    0.001366           1      1966           mprotect
  0.09    0.000837           0      1820           read
  0.09    0.000784           0      5647           close
  0.07    0.000604           0      5215           fstat
  0.06    0.000537           1       493           munmap
  0.05    0.000465           2       244           clone
  0.04    0.000356           1       245       245 access
  0.03    0.000287           2       134           newfstatat
  0.03    0.000235           1       312           openat
  0.02    0.000193           0       743           brk
  0.01    0.000082           0       245           arch_prctl
  0.01    0.000050           0       134           getdents
  0.00    0.000045           0       245           futex
  0.00    0.000041           0       491           rt_sigaction
  0.00    0.000041           0       246           getrlimit
  0.00    0.000040           0       489       244 ioctl
  0.00    0.000038           0       591           fcntl
  0.00    0.000028           0       204       188 lseek
  0.00    0.000024           0       489           set_robust_list
  0.00    0.000013           0       245           rt_sigprocmask
  0.00    0.000012           0       245           set_tid_address
  0.00    0.000000           0         1           uname
  0.00    0.000000           0       245           fchdir
  0.00    0.000000           0         2         1 statfs
------ ----------- ----------- --------- --------- ----------------
100.00    0.902284                 39085      6803 total

sadece grep

> strace -cf grep -r -i 'the brown dog' .
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 40.00    0.000304           2       134           getdents
 31.71    0.000241           0       533           read
 18.82    0.000143           0       319         6 openat
  4.08    0.000031           4         8           mprotect
  3.29    0.000025           0       199       193 lseek
  2.11    0.000016           0       401           close
  0.00    0.000000           0        38        19 open
  0.00    0.000000           0         6         3 stat
  0.00    0.000000           0       333           fstat
  0.00    0.000000           0        32           mmap
  0.00    0.000000           0         4           munmap
  0.00    0.000000           0         6           brk
  0.00    0.000000           0         2           rt_sigaction
  0.00    0.000000           0         1           rt_sigprocmask
  0.00    0.000000           0       245       244 ioctl
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0       471           fcntl
  0.00    0.000000           0         1           getrlimit
  0.00    0.000000           0         1           arch_prctl
  0.00    0.000000           0         1           futex
  0.00    0.000000           0         1           set_tid_address
  0.00    0.000000           0       132           newfstatat
  0.00    0.000000           0         1           set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00    0.000760                  2871       466 total

1
Bir dosya sisteminin tamamını aramada çatallar önemsizdir. G / Ç azaltmak istediğiniz şeydir.
Gilles

OP'den bir hata olmasına rağmen, karşılaştırma yanlış, kullanırken -rbayrağını kaldırmalısınız . Olanların sayısını karşılaştırarak aynı dosyaları tekrar tekrar aradığını görebilirsiniz . grepfindopen
qwertzguy

1
@ qwertzguy, hayır, argümanların hiçbirinin dizin olmadığını garanti -rettiği için zararsız olmalıdır -type f. Birden open()ler tarafından açılan diğer dosyalara aşağı olasılığı daha yüksektir grep(kütüphaneler, yerelleştirme verisi ...) (Btw benim yanıta düzenlemek için teşekkürler) her çağrı sırasında
Stéphane Chazelas

5

Bir SSD'deyseniz ve arama süresi ihmal edilebilirse, GNU’yu paralel olarak kullanabilirsiniz:

find /path -type f | parallel --gnu --workdir "$PWD" -j 8 '
    grep -i -r 'the brown dog' {} 
'

Bu, findbulunanlara bağlı olarak aynı anda 8'e kadar grep işlemini gerçekleştirir .

Bu, bir sabit disk sürücüsüne zarar verebilir, ancak bir SSD onunla iyi başa çıkabilmelidir.


-1

Bu konuda dikkate alınması gereken bir şey daha aşağıdaki gibidir.

Dizinlerinin herhangi Will grep aracılığıyla sisteminizin daha fazla dosya içeriyor yinelemeli gitmek zorunda kalacak nofile ayar? (örneğin, açık dosya tanıtıcılarının sayısı, çoğu linux dağıtımında varsayılan değer 1024'tür)

Öyleyse, o takdirde bulmak kesinlikle belirli sürümleri beri gitmek yoludur grep bir dışarı bombalayacak Argüman listesinin çok uzun maksimum açık dosya ayarını kolları daha dosyaları bir dizine çarptığında hatası.

Sadece benim 2 ¢.


1
Neden grepbombalanacak? En azından GNU grep ile izini sürüp /kullanacağınız bir yol verirseniz -R, dizinler arasında basitçe yineleyin. Kabuk kabuk-globs vermedikçe bir şey genişletmek için gitmiyor. Dolayısıyla verilen örnekte ( /*) yalnızca /maddenin sıralanacağı alt klasörlerin değil , maddenin içeriği grepkabuktan argüman olarak geçilmemiştir.
0xC0000022L

Peki, OP özyinelemeli olarak arama yapmak istediğini düşünüyorsa (örneğin, "grep -r -i ', kahverengi köpek' / *"), GNU'nun grep (en azından Sürüm 2.9) bombalarını şöyle gördüm : "- bash: / bin / grep: Argüman listesi çok uzun ", içinde 140.000'in üzerinde alt dizin bulunan bir dizinde kullanılan OP'nin tam olarak aranması.
B.Kaatz
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.