Bir dizini / metni özyinelemeli olarak yalnızca bir dizinin belirtilen dosya adında mı ararsınız?


16

Ben bir dizin (örneğin, sahip abc/def/efgbirçok alt dizinleri (örneğin ,: ile) abc/def/efg/(1..300)). Bu alt dizinlerin hepsinde ortak bir dosya vardır (örn file.txt.). Ben sadece bu file.txtdiğer dosyaları hariç bir dize aramak istiyorum . Bunu nasıl yapabilirim?

Kullandım grep -arin "pattern" *, ancak çok sayıda alt dizin ve dosyaya sahipsek çok yavaş.


Yanıtlar:


21

Üst dizinde, yalnızca bu dosyaları kullanabilir findve daha sonra çalıştırabilirsiniz grep:

find . -type f -iname "file.txt" -exec grep -Hi "pattern" '{}' +

2
Ben de geçen önermek -Hiçin greptek yol kendisine geçirilen zaman yolunu hala (daha doğrusu dosyadan sadece eşleştirme hatları hariç) basılı olduğundan, durumlarda, böylece.
Eliah Kagan

24

Globstar da kullanabilirsiniz.

Bina grepile komutları findzanna yanıtında olduğu gibi, (ayrıca bkz Bunu yapmak için son derece sağlam, çok yönlü ve taşınabilir bir yoldur sudodus cevabını ). Ve muru, grep'ın --includeseçeneğini kullanma konusunda mükemmel bir yaklaşım yayınladı . Sadece kullanmak istiyorsanız grepkomutu ve kabuk, bunu yapmak için başka bir yol yoktur - Yapabileceğiniz kendisi kabuk gerekli özyineleme :

shopt -s globstar   # you can skip this if you already have globstar turned on
grep -H 'pattern' **/file.txt

-HBayrak markaları grepyalnızca bir eşleşen dosya bulunsa bile dosya adını gösterir. Sen geçebilir -a, -ive -nbayrakları (sizin örnekten) için grepbuysa yanı, sana gereken. Ancak geçmeyin -rveya -Rbu yöntemi kullanırken. Öyle kabuk içeren glob desen genişleyen dizinleri recurses **ve değilgrep .

Bu talimatlar Bash kabuğuna özeldir. Bash, Ubuntu'daki (ve diğer birçok GNU / Linux işletim sistemindeki) varsayılan kullanıcı kabuğudur, bu nedenle Ubuntu'daysanız ve kabuğunuzun ne olduğunu bilmiyorsanız, neredeyse kesinlikle Bash'tir. Popüler mermiler genellikle dizin geçişli **küreleri desteklese de , her zaman aynı şekilde çalışmazlar. Daha fazla bilgi için, bkz Stéphane Chazelas 'ın mükemmel cevabı için ls sonucu *** ** ve ls, ls * üzerine Unix.SE .

Nasıl çalışır

Açılması globstar bash kabuk seçeneği yapar **dizin ayırıcı içeren maç yolları ( /). Bu nedenle, dizin yinelenen bir globtur. Özellikle, man bashaçıkladığı gibi:

Ne zaman globstar kabuk seçeneği etkinleştirildiğinde ve * bir yol adı genişleme bağlamında kullanıldığında, iki bitişik * tüm dosya ve sıfır veya daha fazla dizinleri ve alt dizinleri maç olacak tek bir model olarak kullanılabilir s. Bunu bir / ile takip ederseniz, bitişik iki * yalnızca dizinlerle ve alt dizinlerle eşleşir.

Değiştirmek veya niyetinde çok daha fazla dosyaları silmek komutları çalıştırabilirsiniz beri yazdığınız özellikle eğer, bununla dikkatli olmalıdır **yazmak isterken *. (Hiçbir komutu değiştirmeyen bu komutta güvenlidir.) shopt -u globstarGlobstar kabuk seçeneğini kapatır.

Globstar ve arasında birkaç pratik fark vardır find.

findglobstar'dan çok daha çok yönlüdür. Globstar ile yapabileceğiniz her şeyi, findkomutla da yapabilirsiniz. Globstar'ı severim ve bazen daha uygun olur, ancak globstar genel bir alternatif değildir find.

Yukarıdaki yöntem, adları a ile başlayan dizinlerin içine bakmaz .. Bazen bu klasörleri geri almak istemezsiniz, ama bazen yaparsınız.

Sıradan bir kürede olduğu gibi, kabuk tüm eşleşen yolların bir listesini oluşturur ve bunları grepkürenin kendisi yerine komutunuza ( ) gönderir. file.txtSonuçta ortaya çıkan komutun sistemin yürütülmesi için çok uzun olacağı denilen çok fazla dosyanız varsa , yukarıdaki yöntem başarısız olur. Pratikte (en azından) binlerce dosyaya ihtiyacınız olacak, ancak bu olabilir.

Kullanılan yöntemler findbu kısıtlamaya tabi değildir, çünkü:

  • Zanna'nın yolugrep potansiyel olarak birçok yol argümanı içeren bir komut oluşturur ve çalıştırır . Ancak, tek bir yolda listelenenden daha fazla dosya bulunursa, +-terminated -execeylemi komutu bazı yollarla çalıştırır, daha sonra başka yollarla yeniden çalıştırır. grepBirden fazla dosyada bir dize girilmesi durumunda , bu doğru davranışı sağlar.

    Burada kapsanan globstar yöntemi gibi, bu da eşleşen tüm satırları, her birinin başına yollar eklenmiş olarak yazdırır.

  • sudodus'un yolugrep her file.txtbulunan için ayrı çalışır . Çok fazla dosya varsa, diğer bazı yöntemlerden daha yavaş olabilir, ancak çalışır.

    Bu yöntem dosyaları bulur ve yollarını yazdırır, ardından varsa eşleşen satırları izler. Bu benim yöntem, Zanna ve muru tarafından üretilen biçiminden farklı bir çıktı biçimidir .

İle renklendirme find

Globstar kullanmanın en önemli avantajlarından biri, varsayılan olarak Ubuntu'da greprenklendirilmiş çıktı üretecektir. Ama kolayca ile bu alabilirsiniz findda .

Ubuntu'daki kullanıcı hesapları , gerçekten çalıştırılan ( görmek için çalıştırılan ) bir takma adla oluşturulur . Bu var iyi bir şey takma adlar olduğunu hemen hemen sadece etkileşimli onları kesilirken genişletilmiş , ancak isterseniz demektir çağırmaya ile bayrak, açıkça yazmak gerekecek. Örneğin:grepgrep --color=autoalias grepfindgrep--color

find . -name file.txt -exec grep --color=auto -H 'pattern' {} +

Bunun bashçalışması için kabuğu kullanmanız gerektiğini daha net belirtmek isteyebilirsiniz . Sen do "globstar bash kabuk seçeneği" örtük söyle ancak kolayca çok çabuk okuma kişi tarafından kaçırılmış olabilir.
Stig Hemmer

Cevabımı kaldırdım çünkü çok sayıda eleştirel yoruma neden oldu. Bu yüzden cevabınızdaki referansı kaldırmalısınız.
sudodus

@StigHemmer Teşekkürler - Tüm mermilerin bu özelliği olmadığını açıkladım. Birçok kabuk (sadece bash değil) dizin geçişli globları desteklese de **, temel eleştiriniz doğrudur: **bu cevaptaki sunum bash'ye özgüdür , shopt sadece bash ve "globstar" terimi (sanırım) bash ve sadece tcsh. Başlangıçta bu karmaşıklıklar yüzünden gözlerini kamaştırdım, ama biraz kafa karıştırıcı olduğu konusunda haklısın. Bu cevapta uzunca tartışmak yerine, ağır kaldırma yapan başka (oldukça kapsamlı) bir yazıya bağlandım.
Eliah Kagan

@sudodus Bunu yaptım, ama umarım bu geçicidir. Ben ve diğerleri cevabınızı değerli bulduk. Bu doğru -eyollara uygulanmamalıdır, ancak bu kolaylıkla sabittir. İlk komut için, atlayın -e. İkincisi için find . -name file.txt -printf $'\e[32m%p:\e[0m\n' -exec grep -i "pattern" {} \;veya tuşunu kullanın find . -name file.txt -exec printf '\e[32m%s:\e[0m\n' {} \; -exec grep -i "pattern" {} \;. Kullanıcılar bazen ( -ekullanım sabit olduğunda) eşleşen satır başına bir yol yazdıran yolunu tercih ederler ; sizinki bulunan dosya başına bir yol ve ardından grepsonuçlar yazdırır .
Eliah Kagan

@sudodus Yani grepkendisi olmaz ne yaptığını yapmak. Diğer bazı eleştiriler de yanlıştı. grep -Htarafından çalıştır (veya ) -execolmadan renklenmez . IEEE 1003.1-2008 genişlemeyi garanti etmez , ancak Ubuntu'nun GNU bulması vardır . Sizinle 's ok Eğer ben düzeltmek için post düzenlemek edeceğiz hata (ve kullanma durumu netleştirmek) ve geri getirmek istediğiniz olmadığını görebilirsiniz. (Silinen yayınları görüntülemek / düzenlemek için --colorGREP_COLOR{}##### {}:-e
destek teknisyenim var

18

Buna ihtiyacınız yok find; grepBu mükemmel para cezası ile başa çıkabilir:

grep "pattern" . -airn --include="file.txt"

Gönderen man grep:

--exclude=GLOB
      Skip  files  whose  base  name  matches  GLOB  (using   wildcard
      matching).   A  file-name  glob  can  use  *,  ?,  and [...]  as
      wildcards, and \ to quote  a  wildcard  or  backslash  character
      literally.

--exclude-from=FILE
      Skip  files  whose  base name matches any of the file-name globs
      read from FILE  (using  wildcard  matching  as  described  under
      --exclude).

--exclude-dir=DIR
      Exclude  directories  matching  the  pattern  DIR from recursive
      searches.

--include=GLOB
      Search  only  files whose base name matches GLOB (using wildcard
      matching as described under --exclude).

Güzel - bu en iyi yol gibi görünüyor. Basit ve verimli. Keşke bu yöntemi bilseydim (veya manpage'i kontrol etmeyi düşündüm). Teşekkürler!
Eliah Kagan

@EliahKagan Zanna bunu yayınlamadığı için çok şaşırdım - bir süre önce başka bir cevap için bu seçeneğe bir örnek göstermiştim. :)
muru

2
yavaş öğrenen, ne yazık ki, ama sonunda oraya geliyorum, öğretileriniz tamamen boşa harcanmıyor;)
Zanna

Bu çok basit ve hatırlanması kolay. Teşekkür ederim.
Rajesh Keladimath

Katılıyorum, bu en iyi cevap. Karışıklığı azaltmak için cevabımı kaldırmalı mıyım yoksa alternatifler olduğunu ve neler yapılabileceğini göstermeye devam find?
etmeli miyim

8

Muru'nun cevabında verilen ve bir dosya adı belirtmek grepiçin --includebayrakla çalıştırma yöntemi genellikle en iyi seçimdir. Ancak, bununla da yapılabilir find.

Bu yanıttaki yaklaşım, bulunan her dosya için ayrı ayrı findçalıştırmak grepiçin kullanılır ve her dosyadaki yolu, her dosyada bulunan eşleşen çizgilerin üzerine tam olarak bir kez yazdırır . (Yolu eşleşen her satırın önündeki yazdırma yöntemleri diğer yanıtlarda da yer almaktadır.)


Dizini, bu dosyalara sahip olduğunuz dizin ağacının en üstüne değiştirebilirsiniz. O zaman koş:

find . -name "file.txt" -type f -exec echo "##### {}:" \; -exec grep -i "pattern" {} \;

Bu, .adlandırılan her dosyanın yolunu (geçerli dizine göre ve dosya adının kendisi dahil) ve file.txtardından dosyadaki tüm eşleşen satırları yazdırır . Bu {}, bulunan dosya için bir yer tutucu olduğu için çalışır . Her dosyanın yolu, ön ekiyle önek olarak içeriğinden ayrılır #####ve o dosyadaki eşleşen satırlardan önce yalnızca bir kez yazdırılır. ( file.txtEşleşme içermeyen olarak adlandırılan dosyalar hala yollarını yazdırır.) Bu çıktıyı, eşleşen her satırın başlangıcında bir yol yazdıran yöntemlerden aldığınızdan daha az karmaşık bulabilirsiniz.

Kullanılması findböyle hemen hemen her zaman daha hızlı çalışan daha olacak grepüzerinde her dosyanın ( grep -arin "pattern" *çünkü) finddoğru adla dosyalar için arar ve diğer tüm dosyaları atlar.

Ubuntu GNU find kullanır , hangi zaman genişler {}bunun daha büyük bir dize göründüğünde bile gibi ##### {}:. Bunu desteklemeyebilecek sistemlerde çalışmakfind için komutunuza ihtiyacınız varsa veya -execeylemi yalnızca kesinlikle gerekli olduğunda kullanmayı tercih ediyorsanız, şunları kullanabilirsiniz:

find . -name "file.txt" -type f -printf '##### %p:\n' -exec grep -i "pattern" {} \;

Çıktının daha kolay okunmasını sağlamak için , ANSI kaçış dizilerini kullanarak renkli dosya adları alabilirsiniz. Bu, her dosyanın yol başlığının altına yazdırılan eşleşen satırlardan daha iyi olmasını sağlar:

find . -name file.txt -printf $'\e[32m%p:\e[0m\n' -exec grep -i "pattern" {} \;

Yani senin kabuk neden çevirmek için kaçış kodu terminalde yeşil üreten gerçek kaçış dizisi içine yeşil için ve normal renk için kaçış kodu ile aynı şeyi yapmak. Bu kaçışlar, findbir dosya adı yazdırdığında bunları kullanan aktarılır . ( $' 'Çünkü tırnak burada gerekli findbireyin -printfeylem tanımıyor \eANSI çıkış kodları yorumlamak için.)

İsterseniz, bunun yerine kullanabilirsiniz -execile sistemin printfkomuta (destek yapar ki \e). Yani aynı şeyi yapmanın başka bir yolu:

find . -name file.txt -exec printf '\e[32m%s:\e[0m\n' {} \; -exec grep -i "pattern" {} \;

i bir dizi ile bir "for loop" yapacaktı ve ben exec exec seçeneği hakkında düşünmüyordu. İyi bir! Ama ben nokta kullanmanın sizi zaten bulunduğunuz dizinde bulacağını düşünüyorum. Eğer Yanlışsam beni düzelt. Doğrudan bulma düzeninde ayrıştırmak için belirtmek daha iyi olmaz mıydı? find abc/def/efg -name "file.txt" -type f -exec echo -e "##### {}:" \; -exec grep -i "pattern" {} \;
17'de kcdtv

Tabii, bu cd abc/def/efg'dizini değiştir' komutunu ortadan kaldıracak :-)
sudodus

(1) Neden -eseçeneği belirtiyorsunuz echo? Bu, ters eğik çizgiler içeren dosya adlarını değiştirmesine neden olur. (2) Argümanın bir parçası{} olarak kullanılması garanti edilmez. Bunu söylemek daha iyi olurdu ya . (3) Neden sadece veya değil ? (4) Ayrıca düşünün . -exec echo "#####" {} \;-exec printf "##### %s:\n" {} \;-print-printfgrep -H
G-Man 'Monica'yı Eski durumuna Getir'

@ G-man, 1) Çünkü orijinal olarak ANSI rengini kullandım: find . -name "file.txt" -type f -exec echo -e "\0033[32m{}:\0033[0m" \; -exec grep -i "pattern" {} \;2) Haklı olabilirsin, ama şu ana kadar bu benim için çalışıyor. 3) -print ve -printf de alternatiftir. 4) Bu ana cevapta zaten var. - Her neyse, kendi cevabınızı bekliyoruz :-)
sudodus

İki -execgörüşmeye ihtiyacınız yok . Sadece kullanın grep -Hve bu dosya adını (renkli) ve eşleşen metni yazdıracaktır.
terdon

0

Sadece sorunun koşulları edebi alınabiliyorsa, doğrudan grep kullanabilirsiniz:

grep 'pattern' abc/def/efg/*/file.txt

veya

grep 'pattern' abc/def/efg/{1..300}/file.txt
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.