Desenleri birden fazla satır boyunca nasıl “aşırırım”?


24

Ben kötüye zannediyorum grep/ egrep.

Dizeleri birden çok satırda aramaya çalışıyordum ve aradığım şeyin eşleşmesi gerektiğini bildiğimde eşleşme bulamadım. Başlangıçta regex'lerimin hatalı olduğunu düşündüm ama sonunda bu araçların satır başına çalıştığını okudum (regex'lerim de bu kadar önemsizdi).

Öyleyse, birden çok satırda kalıp aramak için hangi araç kullanılır?



1
@CiroSantilli - Bu Q ve bağlantılı olduğunuzun kopya olduğunu sanmıyorum. Diğer soru ise çok hatlı desen eşleştirmesini nasıl yapacağınızı soruyor (yani bunu yapmak için hangi aracı kullanmalıyım / kullanabilirim) grep. Sıkıca ilişkililer ancak çiftler değil IMO.
slm

@ sim bu davalara karar vermek zor: Anlamanızı görebiliyorum. Bu özel durumun bir kopya olarak daha iyi olduğunu düşünüyorum çünkü kullanıcı "grep""grep" fiilini önerdiğini söyledi ve kabul edilenler de dahil olmak üzere en iyi cevaplar grep kullanmıyor.
Ciro Santilli 事件 改造 32 法轮功 六四 事件

Yanıtlar:


24

İşte sedsize grepbirden çok satırda size benzer davranışlar kazandıracak bir tane :

sed -n '/foo/{:start /bar/!{N;b start};/your_regex/p}' your_file

Nasıl çalışır

  • -n her satırın varsayılan davranışını bastırır
  • /foo/{}eşleşmesini foove dalgalı çizgilerden gelenleri eşleşen çizgilere uymasını söyler. Desenin foobaşlangıç ​​kısmıyla değiştirin .
  • :start regex'in sonunu bulana kadar döngülenmemize yardımcı olacak dallanma etiketidir.
  • /bar/!{}squigglies içinde ne eşleşmeyen çizgiler için yürütecek bar. Desenin barbitiş kısmıyla değiştirin .
  • NBir sonraki satırı aktif tampon belleğe ekler ( sedbuna desen alanı denir)
  • b startstartModel alanı içermediği sürece bir sonraki satırı eklemeye devam etmek için koşulsuz olarak daha önce yarattığımız etikete dallanacaktır bar.
  • /your_regex/pdesen boşluğu eşleşirse yazdırır your_regex. your_regexBirden fazla satırda eşlemek istediğiniz ifadenin tamamı ile değiştirmelisiniz .

1
+1 Bunu alete ekleme! Teşekkürler.
wmorrison365

Not: MacOS'ta bu verirsed: 1: "/foo/{:start /bar/!{N;b ...": unexpected EOF (pending }'s)
Stan James 16

1
Alma sed: unterminated {hatası
Nomaed

Burada karanlıkta @Nomaed Shot, ancak regex herhangi bir "{" karakter içeriyor mu? Eğer öyleyse, onlardan kaçış ters eğik çizgi gerekir.
Joseph R.,

1
@Nomed Bu uygulamalar arasındaki farklar ile ilgisi var gibi görünüyor sed. Yukarıdaki betiği standart uyumlu hale getirmek için bu cevaptaki önerileri izlemeye çalıştım ama bana "başlama" nın tanımsız bir etiket olduğunu söyledi. Bu yüzden bunun standartlara uygun bir şekilde yapılıp yapılmayacağından emin değilim. Yönetirseniz, lütfen cevabımı düzenlemek için çekinmeyin.
Joseph R.

19

Genel olarak veya pcregrepkullanarak linux lezzetinin çoğunda kurulabilen bir araç kullanırım .yumapt

Örneğin

testfileİçeriği olan bir dosyanız varsa, varsayalım.

abc blah
blah blah
def blah
blah blah

Aşağıdaki komutu çalıştırabilirsiniz:

$ pcregrep -M  'abc.*(\n|.)*def' testfile

Birden fazla satırda desen eşleştirme yapmak için.

Üstelik, aynısını da yapabilirsiniz sed.

$ sed -e '/abc/,/def/!d' testfile

5

İşte Perl kullanarak daha basit bir yaklaşım:

perl -e '$f=join("",<>); print $& if $f=~/foo\nbar.*\n/m' file

ya da (JosephR beri aldı sedrota , ben utanmadan onun çalmak olacak öneri )

perl -n000e 'print $& while /^foo.*\nbar.*\n/mg' file

açıklama

$f=join("",<>);: bu işlem dosyanın tamamını okur ve içeriğini (yeni satırlar ve tümü) değişkene kaydeder $f. Daha sonra eşleştirmeye çalışırız ve eşleşirse foo\nbar.*\nyazdırırız (özel değişken $&bulunan son eşleşmeyi tutar). ///mSatırbaşıyla genelinde düzenli ifade maç yapmak için gereklidir.

-0Giriş kayıt ayırıcısını ayarlar. Bunu, 00Perl'in \n\nkayıt ayırıcı olarak ardışık newlines ( ) kullanacağı 'paragraf modunu' etkinleştirmek için ayarlama . Ardışık yeni satırların olmadığı durumlarda, dosyanın tamamı bir kerede okunur (bulamaç).

Uyarı:

Do not büyük dosyalar için bunu yapmak, bu belleğe tüm dosya yüklemek ve bu bir sorun olabilir.


2

Bunu yapmanın bir yolu Perl ile. örneğin işte bir dosyanın içeriği foo:

foo line 1
bar line 2
foo
foo
foo line 5
foo
bar line 6

Şimdi, burada foo ile başlayan herhangi bir çizgiye ve ardından çubukla başlayan herhangi bir çizgiye karşılık gelecek olan bazı Perl'ler:

cat foo | perl -e 'while(<>){$all .= $_}
  while($all =~ /^(foo[^\n]*\nbar[^\n]*\n)/m) {
  print $1; $all =~ s/^(foo[^\n]*\nbar[^\n]*\n)//m;
}'

Perl, bozuldu:

  • while(<>){$all .= $_} Bu, tüm standart girişi değişkene yükler. $all
  • while($all =~Değişken allnormal ifadeye sahipken ...
  • /^(foo[^\n]*\nbar[^\n]*\n)/mRegex: satır başında foo, ardından herhangi bir sayıda yeni satır olmayan karakter, ardından yeni satır, hemen ardından "bar" ve içinde kalan satır bulunan çubuk. /mregex'in sonunda "birden fazla satıra eşleşme" anlamına gelir
  • print $1 Regex'in parantez içindeki kısmını yazdırın (bu durumda tüm normal ifade)
  • s/^(foo[^\n]*\nbar[^\n]*\n)//m Regex için ilk eşleşmeyi silin, böylece söz konusu dosyadaki regex'in birden fazla örneğini eşleştirebiliriz

Ve çıktı:

foo line 1
bar line 2
foo
bar line 6

3
Perl'inizin daha aptalca kısaltabileceğini söylemek için uğradım:perl -n0777E 'say $& while /^foo.*\nbar.*\n/mg' foo
Joseph R.

2

Grep alternatif sift , multiline eşleşmeyi destekler (sorumluluk reddi: Ben yazarım).

Diyelim ki testfile:

<Kitap>
  <title> Lorem Ipsum </title>
  <description> Lorem ipsum dolor amet sitet
  adipiscing elit, sed do eiusmod tempor incididunt ut
  labore et dolore magna aliqua </description>
</ Book>


sift -m '<description>.*?</description>' (açıklamayı içeren satırları göster)

Sonuç:

testfile: <description> Lorem ipsum dolor amet sitet
testfile: adipiscing elit, sed eiusmod tempor incididunt ut
test dosyası: labore et dolore magna aliqua </description>


sift -m '<description>(.*?)</description>' --replace 'description="$1"' --no-filename (tanımı ayıklayın ve yeniden biçimlendirin)

Sonuç:

Description = "Lorem ipsum dolor, oturmak amet, komplo
  adipiscing elit, sed do eiusmod tempor incididunt ut
  Labore et Dolore Magna Aliqua "

1
Çok güzel bir araç. Tebrikler! Ubuntu gibi dağıtımlara dahil etmeye çalışın.
Lourenco

2

Sadece Perl-regexpparametre destekleyen normal bir grep Pbu işi yapacak.

$ echo 'abc blah
blah blah
def blah
blah blah' | grep -oPz  '(?s)abc.*?def'
abc blah
blah blah
def

(?s) regex'inizde sadece karakterleri değil satır sonlarını da eşleştiren nokta yapan DOTALL değiştiricisi olarak adlandırılır.


Bu çözümü denediğimde çıktı 'def' ile bitmiyor ancak 'blah' dosyasının sonuna gidiyor
buckley

belki -P
grep'iniz

1

Bunu benim için grep ve -A seçeneğini başka bir grep ile kullanarak çözdüm.

grep first_line_word -A 1 testfile | grep second_line_word

-A 1 seçeneği, bulunan satırdan sonra 1 satır yazdırır. Tabii ki dosya ve kelime kombinasyonuna bağlıdır. Ama benim için en hızlı ve güvenilir çözümdü.


alias grepp = 'grep --color = auto -B10 -A20 -i' sonra kedi bazı dosyalar | grepp filan | grepp foo | grepp bar ... evet bu -A ve -B çok kullanışlıdırlar ... en iyi cevaba sahipsiniz
Scott Stensland

1

Supppose içeren test.txt dosyasına sahibiz :

blabla
blabla
foo
here
is the
text
to keep between the 2 patterns
bar
blabla
blabla

Aşağıdaki kod kullanılabilir:

sed -n '/foo/,/bar/p' test.txt

Aşağıdaki çıktı için:

foo
here
is the
text
to keep between the 2 patterns
bar

1

Kendileri hariç 2 kalıp arasındaki metni almak istiyorsak.

Supppose içeren test.txt dosyasına sahibiz :

blabla
blabla
foo
here
is the
text
to keep between the 2 patterns
bar
blabla
blabla

Aşağıdaki kod kullanılabilir:

 sed -n '/foo/{
 n
 b gotoloop
 :loop
 N
 :gotoloop
 /bar/!{
 h
 b loop
 }
 /bar/{
 g
 p
 }
 }' test.txt

Aşağıdaki çıktı için:

here
is the
text
to keep between the 2 patterns

Nasıl çalışır, hadi adım adım yapalım

  1. /foo/{ "foo" satırı içerdiğinde tetiklenir
  2. n desen alanını bir sonraki satırla, yani "burada" kelimesiyle değiştirin
  3. b gotoloop "gotoloop" etiketine dallanma
  4. :gotoloop "gotoloop" etiketini tanımlar
  5. /bar/!{ desen "bar" içermiyorsa
  6. h tutma alanını desenle değiştirin, böylece "burada" tutma alanına kaydedilir
  7. b loop "loop" etiketine dal
  8. :loop "loop" etiketini tanımlar
  9. N deseni tutma alanına ekler.
    Şimdi tutun boşluk içerir:
    "burada"
    "" dir.
  10. :gotoloop Şimdi 4. adımdayız ve bir çizgi "bar" içerene kadar döngüdeyiz
  11. /bar/ döngü bitti, "bar" bulundu, kalıp alanı
  12. g desen alanı, ana döngü sırasında kaydedilen "foo" ve "bar" arasındaki tüm satırları içeren tutma alanıyla değiştirilir
  13. p desen alanını standart çıktıya kopyala

Yapıldı!


Aferin +1. Genellikle yeni komutları SOH'ye girerek ve normal sed komutları uygulayarak yeni satırları değiştirerek bu komutları kullanmaktan kaçınırım.
A.Danischewski
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.