Grep kullanarak birden fazla satırda desen nasıl bulunur?


Yanıtlar:


225

Grep bu işlem için yeterli değil.

Modern Linux sistemlerinin çoğunda bulunan pcregrep ,

pcregrep -M  'abc.*(\n|.)*efg' test.txt

nerede -M,--multiline desenlerin birden fazla çizgiyle eşleşmesine izin ver

Daha yeni bir pcre2grep var de var. Her ikisi de PCRE projesi tarafından sağlanmaktadır .

pcre2grep, Mac OS X için bağlantı noktasının bir parçası olarak Mac Bağlantı Noktaları aracılığıyla kullanılabilirpcre2 :

% sudo port install pcre2 

ve Homebrew aracılığıyla :

% brew install pcre

veya pcre2 için

% brew install pcre2

pcre2grep Linux'ta da mevcuttur (Ubuntu 18.04+)

$ sudo apt install pcre2-utils # PCRE2
$ sudo apt install pcregrep    # Older PCRE

11
@StevenLu -M, --multiline- Desenlerin birden fazla çizgiyle eşleşmesine izin ver.
yüzük taşıyıcısı

7
. * (\ N |.) * Öğesinin (\ n |.) * İle eşdeğer olduğunu ve ikincisinin daha kısa olduğunu unutmayın. Ayrıca benim sistemde, daha uzun sürümü çalıştırdığınızda "pcre_exec () hatası -8" oluşur. Bunun yerine 'abc (\ n |.) * Efg' yi deneyin!
daveagp

6
Bu durumda ifadeyi açgözlü yapmamanız gerekir:'abc.*(\n|.)*?efg'
ring bearer

4
ve ilkini atlayabilirsiniz .*-> 'abc(\n|.)*?efg'normal ifadeyi kısaltmak (ve bilgiç olmak)
Michi

6
pcregrepişleri kolaylaştırır, ama aynı grepzamanda işe yarar. Örneğin, bkz. Stackoverflow.com/a/7167115/123695
Michael Mior

113

Grep ile mümkün olup olmadığından emin değilim, ama sed bunu çok kolay hale getiriyor:

sed -e '/abc/,/efg/!d' [file-with-content]

4
Bu dosyaları bulamaz, eşleşen parçayı tek bir dosyadan döndürür
shiggity

11
@Lj. lütfen bu komutu açıklayabilir misiniz? Ben aşinayım sed, ama daha önce böyle bir ifade görmediysem.
Anthony

1
@ Anthony, sed adresinin man sayfasında belgelenmiştir. / Abc / & / efg / 'nin bir adres olduğunu anlamak önemlidir.
Kalamar

49
Bu cevabın biraz daha fazla açıklaması olsaydı faydalı olacağını sanıyorum ve bu durumda bir kez daha oy verirdim. Biraz sed biliyorum, ama yarım saat süren bir uğraştan sonra anlamlı bir çıkış kodu üretmek için bu cevabı kullanmak için yeterli değil. İpucu: 'RTFM', önceki yorumunuzun gösterdiği gibi, StackOverflow'da nadiren oy alır.
Michael Scheper

25
Örnekle ilgili hızlı açıklama: sed '1,5d': 1 ile 5 arasındaki satırları sil. Sed '1,5! D': 1 ile 5 arasında olmayan satırları silme (yani satırları arasında tutma) sonra sayı yerine, / pattern / ile bir satır arayın. Ayrıca aşağıdaki daha basit olana bakın: sed -n '/ abc /, / efg / p' p yazdırma içindir ve -n bayrağı tüm satırları göstermiyor
phil_w

86

İşte bu cevaptan esinlenen bir çözüm :

  • 'abc' ve 'efg' aynı satırda olabilirse:

    grep -zl 'abc.*efg' <your list of files>
  • 'abc' ve 'efg' farklı satırlarda olmalıdır:

    grep -Pzl '(?s)abc.*\n.*efg' <your list of files>

parametreler:

  • -zGirdiyi, her biri yeni satır yerine sıfır bayt ile sonlanan bir satır kümesi olarak ele alın. yani grep, girdiye büyük bir satır gibi davranır.

  • -l çıktıların normal olarak yazdırılacağı her girdi dosyasının baskı adı.

  • (?s)PCRE_DOTALL işlevini etkinleştirin, yani '.' herhangi bir karakter veya yeni satır bulur.


@syntaxerror Hayır, bence bu sadece küçük bir harf l. AFAIK sayı -1seçeneği yoktur .
Sparhawk

Ne de olsa haklısın, belki test ederken bir yazım hatası yapmıştım. Her durumda sahte bir iz bıraktığım için üzgünüm.
sözdizimi hatası

6
Bu mükemmel. Bununla ilgili sadece bir sorum var. Eğer -zseçenekler yeni satırları tedavi etmek için grep belirtirse, zero byte charactersneden (?s)normal ifadeye ihtiyacımız var ? Zaten yeni satır olmayan bir karakterse, .doğrudan eşleştirilememesi gerekir mi?
Durga Swaroop

1
-z (aka - null-data) ve (? s) çok satırlıyı standart bir grep ile eşleştirmek için ihtiyacınız olan şeydir. MacOS'taki kullanıcılar, lütfen sistemlerinizde -z veya --null veri seçeneklerinin kullanılabilirliği hakkında yorum bırakın!
Zeke Fast

4
-z MacOS'ta kesinlikle mevcut değil
Dylan Nicholson

33

sed yukarıda belirtilen LJ posteri gibi yeterli olmalı,

yerine! d yazdırmak için sadece p tuşunu kullanabilirsiniz:

sed -n '/abc/,/efg/p' file

16

Ben pcregrep üzerine büyük ölçüde güveniyordum, ama yeni grep ile birçok özelliği için pcregrep yüklemenize gerek yok. Sadece kullangrep -P .

OP sorusu örneğinde, aşağıdaki seçeneklerin iyi çalıştığını düşünüyorum, soruyu nasıl anladığımla en iyi ikinci eşleşme:

grep -Pzo "abc(.|\n)*efg" /tmp/tes*
grep -Pzl "abc(.|\n)*efg" /tmp/tes*

Metni / tmp / test1 olarak kopyaladım ve 'g'yi sildim ve / tmp / test2 olarak kaydettim. Burada, birincisinin eşleşen dizeyi ve ikincisinin yalnızca dosya adını gösterdiği (tipik -o eşleşmeyi göstermek ve tipik -l yalnızca dosya adını göstermek) gösteren çıktıdır. 'Z' öğesinin çok satırlı satır için gerekli olduğunu ve '(. | \ N)' ifadesinin 'yeni satırdan farklı' veya 'yeni satırdan' eşleşmesi anlamına geldiğini unutmayın:

user@host:~$ grep -Pzo "abc(.|\n)*efg" /tmp/tes*
/tmp/test1:abc blah
blah blah..
blah blah..
blah blah..
blah efg
user@host:~$ grep -Pzl "abc(.|\n)*efg" /tmp/tes*
/tmp/test1

Sürümünüzün yeterince yeni olup olmadığını belirlemek için, çalıştırın man grepve buna benzer bir şeyin üstte görünüp görünmediğine bakın:

   -P, --perl-regexp
          Interpret  PATTERN  as a Perl regular expression (PCRE, see
          below).  This is highly experimental and grep -P may warn of
          unimplemented features.

Bu GNU grep 2.10'dan.


14

Bu, önce tryeni satırları başka bir karakterle değiştirmek için kullanılarak kolayca yapılabilir :

tr '\n' '\a' | grep -o 'abc.*def' | tr '\a' '\n'

Burada, \ayeni satır yerine alarm karakterini (ASCII 7) kullanıyorum. Bu, metninizde neredeyse hiç bulunmaz ve greponu a ile .eşleştirebilir veya özellikle ile eşleştirebilir \a.


1
Bu benim yaklaşımımdı ama kullanıyordum \0ve bu yüzden ihtiyaç duyuyordum grep -ave eşleştirdim \x00… Basitleştirmeme yardım ettin! echo $log | tr '\n' '\0' | grep -aoE "Error: .*?\x00Installing .*? has failed\!" | tr '\0' '\n'şimdiecho $log | tr '\n' '\a' | grep -oE "Error: .*?\aInstalling .*? has failed\!" | tr '\a' '\n'
Charlie Gorichanaz

1
Kullanın grep -o.
kyb

7

awk tek astarlı:

awk '/abc/,/efg/' [file-with-content]

4
abcBitiş deseni dosyada yoksa veya son bitiş deseni eksikse, dosya baştan sona dosyanın sonuna kadar yazdırılır . Bunu düzeltebilirsiniz, ancak komut dosyasını oldukça önemli ölçüde karmaşıklaştıracaktır.
tripleee

/efg/Çıktıdan nasıl hariç tutulur?
kyb

6

Perl'i kullanabiliyorsanız bunu çok kolay yapabilirsiniz.

perl -ne 'if (/abc/) { $abc = 1; next }; print "Found in $ARGV\n" if ($abc && /efg/); }' yourfilename.txt

Bunu tek bir normal ifade ile de yapabilirsiniz, ancak bu dosyanın tüm içeriğini tek bir dizeye almayı içerir, bu da büyük dosyalarla çok fazla bellek alabilir. Tamlık için, bu yöntem şöyledir:

perl -e '@lines = <>; $content = join("", @lines); print "Found in $ARGV\n" if ($content =~ /abc.*efg/s);' yourfilename.txt

Bulunan ikinci cevap, birkaç satırdaki maçlarla bütün bir çok satırlı bloğu çıkarmak için yararlı oldu - .*?minimum eşleşme elde etmek için açgözlü olmayan eşleştirme ( ) kullanmak zorunda kaldı .
RichVel

5

Bunu grep ile nasıl yapacağımı bilmiyorum, ama awk ile böyle bir şey yaparım:

awk '/abc/{ln1=NR} /efg/{ln2=NR} END{if(ln1 && ln2 && ln1 < ln2){print "found"}else{print "not found"}}' foo

Ancak bunu nasıl yaptığınıza dikkat etmelisiniz. Normal ifadenin alt dize veya tüm sözcükle eşleşmesini ister misiniz? \ w etiketlerini uygun şekilde ekleyin. Ayrıca, bu kesinlikle örneği belirttiğinize uygun olsa da, abc efg'den sonra ikinci kez göründüğünde pek işe yaramaz. Bunu ele almak istiyorsanız, / abc / case vb. İçine uygunsa bir if ekleyin.


3

Ne yazık ki yapamazsınız. Gönderen grepdocs:

grep , belirtilen DİKİŞ ÇEŞİDİ ile eşleşen satırlar için adlandırılmış giriş DOSYALARINI (veya hiçbir dosya adlandırılmamışsa veya dosya adı olarak tek bir kısa çizgi (-) verilirse standart girişi) arar .


ne dersingrep -Pz
Navaro

3

Bağlam kullanmaya istekli olursanız, bunu yazarak yapabilirsiniz.

grep -A 500 abc test.txt | grep -B 500 efg

Bu , birbirlerinin 500 satırında oldukları sürece "abc" ve "efg" arasındaki her şeyi görüntüler .


3

Her iki kelimenin birbirine yakın olması gerekiyorsa, örneğin 3 satırdan fazla değilse, bunu yapabilirsiniz:

find . -exec grep -Hn -C 3 "abc" {} \; | grep -C 3 "efg"

Aynı örnek ancak yalnızca * .txt dosyalarını filtreleme:

find . -name *.txt -exec grep -Hn -C 3 "abc" {} \; | grep -C 3 "efg"

Ayrıca , normal ifadelerle de bulmak istiyorsanız komutu grepkomut ile değiştirebilirsiniz egrep.


3

Birkaç gün önce bunu çok satırlı eşleştirme veya koşullar kullanarak doğrudan destekleyen bir grep alternatifi yayınladım - umarım burada arama yapan bazı insanlar için yararlıdır. Örnek için komutlar şöyle görünecektir:

Çok satırlı:

sift -lm 'abc.*efg' testfile

Koşullar:

sift -l 'abc' testfile --followed-by 'efg'

Ayrıca 'efg' nin belirli sayıda satırda 'abc'yi izlemesi gerektiğini de belirtebilirsiniz:

sift -l 'abc' testfile --followed-within 5:'efg'

Sift-tool.org hakkında daha fazla bilgi bulabilirsiniz .


İlk örneğin sift -lm 'abc.*efg' testfileişe yaradığını düşünmüyorum , çünkü maç açgözlü ve efgdosyadaki sonuncuya kadar tüm satırları silip süpürüyor .
Dr. Alex RE

2

Sed seçeneği en basit ve en kolay olsa da, LJ'nin tek astarı ne yazık ki en taşınabilir değil. C Shell'in bir sürümü ile sıkışmış olanların patlamalarından kaçmaları gerekecek:

sed -e '/abc/,/efg/\!d' [file]

Bu maalesef bash ve ark.


1
#!/bin/bash
shopt -s nullglob
for file in *
do
 r=$(awk '/abc/{f=1}/efg/{g=1;exit}END{print g&&f ?1:0}' file)
 if [ "$r" -eq 1 ];then
   echo "Found pattern in $file"
 else
   echo "not found"
 fi
done

1

desen sırasına meraklı değilseniz grep kullanabilirsiniz.

grep -l "pattern1" filepattern*.* | xargs grep "pattern2"

misal

grep -l "vector" *.cpp | xargs grep "map"

grep -lilk kalıpla eşleşen tüm dosyaları bulur ve xargs ikinci kalıp için grep olur. Bu yardımcı olur umarım.


1
Bu dosyada "pattern1" ve "pattern2" sırasını görmezden gelir - OP özellikle yalnızca "pattern1" SONRA "pattern1" göründüğü dosyaların eşleşmesi gerektiğini belirtir.
Emil Lundberg

1

İle gümüş arama yapan :

ag 'abc.*(\n|.)*efg'

halka sahibinin cevabına benzer, ama bunun yerine ag ile. Gümüş arayıcının hız avantajları muhtemelen burada parlayabilir.


1
Bu işe yaramıyor gibi görünüyor. (echo abctest; echo efg)|ag 'abc.*(\n|.)*efg'uyuşmuyor
phiresky

1

Bunu grep için -P seçeneğini kullanarak çok fasta bir dosyadan bir fasta dizisi ayıklamak için kullanılır:

grep -Pzo ">tig00000034[^>]+"  file.fasta > desired_sequence.fasta
  • Perl tabanlı aramalar için P
  • satır sonu yerine newline char yerine 0 bayt
  • o sadece grep tüm satırı döndürdüğünden beri eşleşenleri yakalamak için (bu durumda -z yaptığından beri bütün dosyadır).

Normal ifadenin özü, [^>]"sembolden daha büyük değil" anlamına gelen


0

Balu Mohan cevabı alternatif olarak, desen düzeni yalnızca kullanarak uygulamak mümkündür grep, headve tail:

for f in FILEGLOB; do tail $f -n +$(grep -n "pattern1" $f | head -n1 | cut -d : -f 1) 2>/dev/null | grep "pattern2" &>/dev/null && echo $f; done

Yine de bu çok hoş değil. Daha okunaklı biçimlendirilmiş:

for f in FILEGLOB; do
    tail $f -n +$(grep -n "pattern1" $f | head -n1 | cut -d : -f 1) 2>/dev/null \
    | grep -q "pattern2" \
    && echo $f
done

Bu, tüm dosyaların isimlerini yazdırır "pattern2"sonra görünen "pattern1", ya da nereye her ikisi de aynı satırda görünür :

$ echo "abc
def" > a.txt
$ echo "def
abc" > b.txt
$ echo "abcdef" > c.txt; echo "defabc" > d.txt
$ for f in *.txt; do tail $f -n +$(grep -n "abc" $f | head -n1 | cut -d : -f 1) 2>/dev/null | grep -q "def" && echo $f; done
a.txt
c.txt
d.txt

açıklama

  • tail -n +i- iinci sonrası tüm satırları yazdır
  • grep -n - eşleşen satırları satır numaralarıyla başa ekle
  • head -n1 - sadece ilk satırı yazdır
  • cut -d : -f 1- :sınırlayıcı olarak kullanarak ilk kesilen sütunu yazdırın
  • 2>/dev/null- ifade boş döndüğünde tailoluşan sessizlik hata çıkışı$()
  • grep -q- grepyalnızca çıkış kodu ile ilgilendiğimiz için bir eşleşme bulunursa hemen susturun ve geri dönün

Herkes açıklayabilir &>misiniz? Ben de kullanıyorum, ama hiçbir yerde belgelendiğini hiç görmedim. BTW, neden grep'i bu şekilde susturmamız gerekiyor? grep -qhile yapmayacak mısın?
sözdizimi

1
&>bash'ye hem standart çıkışı hem de standart hatayı yeniden yönlendirmesini söyler, bkz. bash kılavuzundaki REIRECTION. İyi yakalama grep -q ...yerine yapabileceğimiz konusunda çok haklısın grep ... &>/dev/null!
Emil Lundberg

Öyle düşünmüştüm. Çok fazla garip ekstra yazmanın acısını ortadan kaldıracak. Açıklama için teşekkürler - bu yüzden kılavuzda biraz atlamış olmalıyım. (Bir süre önce uzaktan ilgili bir şeye baktım.) --- Cevabınızda değiştirmeyi bile düşünebilirsiniz. :)
sözdizimi

0

Bu da işe yaramalı mı ?!

perl -lpne 'print $ARGV if /abc.*?efg/s' file_list

$ARGVfile_list /syeni satırda değiştirici aramalarından okurken geçerli dosyanın adını içerir .


0

Dosya deseni *.sh, dizinlerin denetlenmesini önlemek için önemlidir. Elbette bazı testler bunu önleyebilir.

for f in *.sh
do
  a=$( grep -n -m1 abc $f )
  test -n "${a}" && z=$( grep -n efg $f | tail -n 1) || continue 
  (( ((${z/:*/}-${a/:*/})) > 0 )) && echo $f
done

The

grep -n -m1 abc $f 

maksimum 1 eşleşmeyi arar ve satır numarasını (-n) döndürür. Eğer bir maç bulundu (test -n ...) son maç bul efg (Tüm bulmak ve son kuyruk -n 1 ile almak).

z=$( grep -n efg $f | tail -n 1)

başka devam.

Sonuç 18:foofile.sh String alf="abc";":" dan satır sonuna kadar kesmemiz gereken bir şey olduğu için .

((${z/:*/}-${a/:*/}))

2. ifadenin son eşleşmesi ilk eşleşmenin ilk eşleşmesini geçerse olumlu bir sonuç döndürmelidir.

Sonra dosya adını bildiririz echo $f.


0

Neden basit bir şey olmasın:

egrep -o 'abc|efg' $file | grep -A1 abc | grep efg | wc -l

0 veya pozitif bir tamsayı döndürür.

egrep -o (Yalnızca eşleşmeleri gösterir, hile: aynı satırdaki birden çok eşleşme, farklı satırlardaki gibi çok satırlı çıktı üretir)

  • grep -A1 abc (abc'yi ve sonraki satırı yazdırın)

  • grep efg | wc -l (Aynı veya sonraki satırlarda abc'den sonra bulunan 0-n efg satırı sayısı, sonuç bir "if" değerinde kullanılabilir)

  • desen eşleşmesi gerekiyorsa grep, egrep vb. olarak değiştirilebilir.


0

Aradığınız 2 'abc' ve 'efg' dizeleri arasındaki mesafe hakkında bazı tahminleriniz varsa, şunları kullanabilirsiniz:

grep -r . -e 'abc' -A num1 -B num2 | grep 'efg'

Bu şekilde, ilk grep satırdan sonra 'abc' artı # num1 satır ve ondan sonra # num2 satır döndürür ve ikinci grep 'efg' elde etmek için tüm bunları gözden geçirir. Ardından hangi dosyalarda birlikte göründüklerini bileceksiniz.


0

Birkaç ay önce yayınlanan ugrep ile :

ugrep 'abc(\n|.)+?efg'

Bu araç hız için son derece optimize edilmiştir. Ayrıca GNU / BSD / PCRE-grep uyumludur.

+?Tüm satırları dosyadaki efgsonuncuya kadar eşleştirmek istemiyorsanız, tembel bir tekrar kullanmamız gerektiğini unutmayın efg.


-3

Bu çalışmalı:

cat FILE | egrep 'abc|efg'

Birden fazla eşleşme varsa grep -v kullanarak filtreleyebilirsiniz


2
Bu kod parçacığı açığız ve bazı yardım sağlamak görülebilir fakat bunun olacağını bunun bir açıklama dahil eğer büyük ölçüde geliştirilmiş bir nasıl ve niçin bu çözer sorunu. Sadece şu anda soran kişi için değil, gelecekte okuyucular için soruyu cevapladığınızı unutmayın! Lütfen açıklama eklemek için cevabınızı düzenleyin ve hangi sınırlamaların ve varsayımların geçerli olduğunu belirtin.
Toby Speight

1
Bu , soruda belirtildiği gibi, aslında birden fazla satırda arama yapmaz .
n.st
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.