Belirli bir önceden bağlam almak için Bash betiği


0

Gerçekleştirilen eylem gruplarının olduğu bazı günlük dosyalarını araştırıyorum. Her grubun başlangıcında, grup hakkında bilgi sahibi olan bir satır var ve daha sonra her bir eylem hakkında ayrıntılı bilgi verilmesi, her testin sonunda basılan bir PASS / FAIL durumu ile yazdırılıyor.

Yapmak istediğim, başarısız olan eylemleri bulmak ve başarısızlık satırından önce bir miktar bağlam izleyen başlık satırını yazdırmak.

Örneğin:

Start test group ID 12345
verbose info
verbose info
Test 1 PASSED
verbose info
verbose info
Test 2 PASSED
Start test group ID 238284
verbose info
verbose info
Test 1 PASSED
verbose info
verbose info
Test 2 FAILED

Yukarıdakiler yoğunlaşacaktır.

Start test group ID 238284
verbose info
verbose info
Test 2 FAILED

FAILED işaretinden önceki satır sayısı her test için sabit değildir ve her test de farklı bir ortalama uzunluktur, ancak sabit bir sayı benim için iyidir. Genelde sadece son birkaç satırla ilgileniyorum.

Bunun grep için biraz karmaşık olabileceğini hissediyorum, ancak hiçbir şey için gerçekten awk kullanmadım ve nereden başlayacağımı bilmiyorum.

Yanıtlar:


3

awkÇıktıyı tersten işleyerek işleri basitleştiren bir çözüm ( tacGNU çekirdeklerinin bir parçası olan komutu gerektirir ):

İlk önce, awkscript ('process.awk' gibi bir dosyaya yerleştirin). Bir bashliner için sadece biraz fazla uzun .

BEGIN                           { output=0; any=0; }
/^Test .* FAILED/               { output=1; any=1; }
/^Test .* PASSED/               { output=0; }
/^Start test group/ && any == 1 { output=1; any=0; }
output == 1                     { print; }

Ardından, ters çevrilmiş günlük dosyasında bu betiği çalıştırın ve çıktıyı ters çevirin:

tac logfile | awk -f process.awk | tac

O nasıl çalışır?

Öncelikle tacsatırların sırasını tersine çevirmek için girdilerimizden geçiyoruz (böylece "takip eden" satırların okumadan önce BAŞARILI veya GEÇİŞİ testine ait olup olmadığını belirleyebiliriz).

Komut aşağıdaki gibi çalışır. Her eylem, eşleşmesi gereken bir koşuldan sonra geçerli satır koşulla eşleşiyorsa yürütülecek kod bloğundan oluşur.

İlk eylem, girdiye bakmaya başlamadan önce her zaman bir kez yürütülen bir BEGIN işlemidir. Basılı olanı kontrol eden iki boolean bayrağı başlattı. outputMevcut satırı yazdırmak istiyorsak 1 olarak, aksi takdirde 0 olarak ayarlanacaktır. anyHIZLI bir testle karşılaştığımız her zaman 1 olarak ayarlanacak ve bir test grubunu işlemeyi bitirdikten sonra 0 olarak sıfırlanacaktır. Her iki değer de 0'dan başlar.

Bir sonraki eylem, başarısız bir testin başlangıcı olup olmadığını görmek için geçerli satırı test eder (unutmayın, çıktıyı tersine işliyoruz). Eğer öyleyse, hem set outputve any.

Bir sonraki eylem, geçilen bir testin başlangıcı olup olmadığını görmek için geçerli satırı test eder. Eğer öyleyse, outputbayrağı temizleyin , ancak anyyalnız bırakın . (Test grubunun bitiminden önce hala başarısız bir test olabilir).

Sonraki eylem, test grubu başlığı olup olmadığını ve anybayrağın ayarlanıp ayarlanmadığını görmek için geçerli satırı test eder . Eğer öyleyse, başlığı yazdırmak istiyoruz (en az bir başarısız testimiz vardı), bu yüzden ayarlayın outputve temizleyin any(bir sonraki test grubuna hazırlanmak için). Aksi takdirde, hiçbir şey yapmamız gerekmez; anyZaten 0'dır ve outputayarlanmış olamaz 1eğer anyolmadı.

Son olarak, şu anki çizgiye bakmayan bir eylemimiz var, ancak önceki eylemlerden herhangi birinin ayarlanmış olup olmadığını kontrol ediyoruz output. Varsa, şu anki satırı basarız (bu bir "Test Başarısız" satırı, "FAILED" satırından önce gelen bazı ayrıntılı bilgiler veya bir test grubu başlığı).

Tüm işlemler bittikten sonra, bir sonraki giriş satırına geçer ve her işlemi tekrar uygulamaya çalışırız. Tüm girdiler tükendikten sonra, istediğimiz her çıktı satırını ancak ters sırada yazdırırız. Çıkışı borulara bağlar, tacbunu düzeltir.

Senaryoyu daha karmaşık hale getirme pahasına biraz daha verimli yapılabileceğini, ancak yeterince hızlı olması gerektiğini unutmayın.


+1, uzun açıklama ile kompakt AWK ve tacdaha kolay ayrıştırma için güzel bir numara .
nik

1

Bash'ta Bgs'in betiğinden türetilmiş bir script :

buffer=""; cat /your/file | while read line
do
    echo $line | grep -Eq "^Start" && start=$line && continue
    echo $line | grep -q "FAILED" && echo -e "$start$buffer\n$line" \
            && buffer="" && continue
    echo $line | grep -q "PASSED" && buffer="" || buffer="$buffer\n$line"
done

Her "FAILED" satırı için "Başlat" satırı, "FAILED" satırından önceki tüm satırlarla birlikte, son "FAILED" veya "PASSED" satırına (hariç) yazdırılır.

Örnek giriş dosyası:

Start test group ID 12345
verbose info #1
verbose info #2
Test 1 PASSED
verbose info #3
verbose info #4
Test 2 PASSED
verbose info #5
verbose info #6
Test 3 PASSED
verbose info #7
verbose info #8
verbose info #9
Test 4 FAILED
Start test group ID 98765
verbose info #10
verbose info #11
verbose info #12
Test 5 FAILED
verbose info #13
verbose info #14
Test 6 PASSED
verbose info #15
verbose info #16
verbose info #17
Test 7 FAILED
verbose info #18
verbose info #19
verbose info #20
Test 8 PASSED

Komut dosyası çıktısı:

Start test group ID 12345
verbose info #7
verbose info #8
verbose info #9
Test 4 FAILED
Start test group ID 98765
verbose info #10
verbose info #11
verbose info #12
Test 5 FAILED
Start test group ID 98765
verbose info #15
verbose info #16
verbose info #17
Test 7 FAILED

Bu Golf çalışması, Unix ortamının araçlarının neden önemli olduğunu gösteriyor. Cevabına karşı bir şey yok speakr. Başka bir cevap Perl / Python / vs iyi bu bir tamamlayacak:-)
nik

1
@nik Perl: cat /your/file | perl -e 'while(<>){if($_=~m/^Start/){$s=$_;next;}if($_=~m/FAILED/){print$s.$b.$_;$b="";next;}$b=($_=~m/PASSED/)?"":$b.$_;}'aynı çıktıyı verir. ;-)
speakr

@ speakr'ın Perl çözümü: Güzel :-D
Daniel Andersson

0

Fikirleri düşünerek ... buraya gelmenin birkaç yolu var.

  1. Başlangıç ​​çizgilerini yakalamak için AWK'yi kullanın ve ardından Başlangıç ​​çizgisini ve BAŞARISIZ çizgisine giden son paketi attığınız noktada bir BAŞARISIZ olana kadar son GEÇİŞLİ veya BAŞARISIZ çizgiden itibaren yakalayın.

  2. Veya, Start satırlarını ve FAILED içeriğini grep ile ayrı ayrı filtreleyin ve birleştirin. Bunun için satır numaralarını korumanız gerekecektir.

Bu ham AWK'yi başlangıç ​​olarak deneyin ,

# script.awk
BEGIN {buffer1="";buffer2=""}
{ 
 if ($1 == "Start") 
 {
  buffer1=$0
 } 
 else 
 { 
  if ($3 == "PASSED") 
  {
   buffer2=""
  } 
  else 
  {
   buffer2=buffer2 "\n" $0; 
   if ($3 == "FAILED") 
   {
    printf "%s%s\n",buffer1,buffer2
   }
  }
 }
}

İle koş awk -f script.awk file.txt

Notlar:

  1. Sizin veya satırlarınız farklıysa Start, bunun yapılması gerekecekPASSEDFAILED
    • Örneğindeki kadar tutarlılarsa oldukça ileri
  2. Bu, verboseparçalarınızın 'doğru' yerde yukarıdaki 3 'anahtar kelimeden' birine sahip olması durumunda yanlış olabilir .
    • öyleyse, doğru anahtar kelimeleri yakalamak için daha fazla bağlam eklemeniz gerekir.
  3. Bu, tüm FAILED bölüm çizgilerini alır.
    • Daha az veri elde etmek için arabelleklerle biraz oynayabilirsiniz

0

Basit bir bash çözümü (Örneğinizi foo.txt dosyasına kaydettim):

buff="" ; cat foo.txt| while read line; do echo $line| grep -q "^Start" && buff="" ; buff="$buff\n$line" ; echo $line | grep -q FAILED && echo -e $buff; done

hoşgeldin Bgs. Beklenen çıktıyla bununla eşleştin mi?
nik

Evet: # buff="" ; cat foo.txt| while read line; do echo $line| grep -q "^Start" && buff="" ; buff="$buff\n$line" ; echo $line | grep -q FAILED && echo -e $buff; done Start test group ID 238284 verbose info verbose info Test 1 PASSED verbose info verbose info Test 2 FAILEDBiçimlendirme yorumdan geçmiyor, ancak çıktı olması gerektiği gibi çok satırlı :)
Bgs

@Bgs Komut dosyanız giriş dosyasının her satırını arabelleğe alır ve her satırda tüm arabelleği yazdırır FAILED. Bunun istenen sonuç olduğunu sanmıyorum.
speakr

Ben'in dediği gibi 'Çıktının yalnızca son birkaç satırına sahip olmayı umursamadı' Kayıt filtrelemeyi hızlandırabilen daha az dış çağrı yapmaya gittim. Aslında girdilerin ne kadar büyük olduğunu bilmiyoruz. Gigabayt günlükleri filtrelemek zorunda kalırsa, daha az çağrı daha iyidir. Yalnızca birkaç yüz kilobayt, daha iyi çıktı IMHO :) ise daha iyi olur (Örnek: echo bir bash yerleşiktir, grep bir dış aramadır).
Bgs,
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.