Günlükte bir metin izlemenin ve komutta bir metin göründüğünde komut çalıştırmanın en iyi yolu


54

Sunucu çalışırken belirli bir metin satırını günlük dosyasına gönderen bir sunucu günlüğüne sahibim. Sunucu açıldıktan sonra bir komut çalıştırmak istiyorum ve bu nedenle aşağıdakine benzer bir şey yapın:

tail -f /path/to/serverLog | grep "server is up" ...(now, e.g., wget on server)?

Bunu yapmanın en iyi yolu nedir?


3
tail -Fkütük dönüşünü idare etmek için kullandığınızdan emin olun - yani my.logdolgun hale gelir ve ilerler my.log.1ve süreciniz yeni bir şey yaratırmy.log
sg

Yanıtlar:


34

Basit bir yol, garip olurdu.

tail -f /path/to/serverLog | awk '
                    /Printer is on fire!/ { system("shutdown -h now") }
                    /new USB high speed/  { system("echo \"New USB\" | mail admin") }'

Ve evet, ikisi de bir çekirdek kütüğünden gelen gerçek mesajlar. Perl bunun için kullanmak için biraz daha şık olabilir ve kuyruk ihtiyacının yerini alabilir. Perl kullanıyorsanız, şuna benzer bir şey görünecektir:

open(my $fd, "<", "/path/to/serverLog") or die "Can't open log";
while(1) {
    if(eof $fd) {
        sleep 1;
        $fd->clearerr;
        next;
    }
    my $line = <$fd>;
    chomp($line);
    if($line =~ /Printer is on fire!/) {
        system("shutdown -h now");
    } elsif($line =~ /new USB high speed/) {
        system("echo \"New USB\" | mail admin");
    }
}

awkTek satırda anında kısa ve kolay olması için çözümü seviyorum . Bununla birlikte, çalıştırmak istediğim komutun alıntıları varsa, bu biraz hantal olabilir, alternatif olabilir, belki kısa bir tek liner çözümüne izin veren ancak sonuçlanan komutun iletilmesini gerektirmeyen boru hatları ve bileşik komutlar kullanarak dize olarak mı?
jonderry

1
@ jon awk'yi bir komut dosyası olarak yazabilirsiniz. Komut dosyasının ilk satırı olarak "#! / Usr / bin awk -f" komutunu kullanın. Bu benim örneğimdeki dış tek tırnak işareti ihtiyacını ortadan kaldıracak ve onları bir system()komut içinde kullanım için serbest bırakacaktır .
penguin359

@ penguin359, Doğru, ancak komut satırından da yapılması ilginç olabilir. Benim durumumda, öngöremediğim birçok şeyi içeren yapmak istediğim farklı şeyler var, bu yüzden sunucuyu başlatıp hepsini tek bir satırda yapabilmek uygun.
jonderry

Ne kadar sağlam olduğunu bilmeme rağmen bir alternatif buldum:tail -f /path/to/serverLog | grep "server is up" | head -1 && do_some_command
jonderry

@ jon Bu şekilde kafa kullanarak biraz kırılgan görünüyor. Daha da önemlisi, örneklerim gibi tekrarlanamaz. Eğer "sunucu çalışıyor", kütüğün son on satırında ise, komutu çalıştırır ve derhal çıkar. Yeniden başlatırsanız, büyük olasılıkla "sunucu" içermeyen on satır günlüğe eklenmemişse, yeniden başlatılır ve tekrar çıkar. Bunun daha iyi sonuç verebileceğinin bir modifikasyonu tail -n 0 -f /path/to/serverLog , Dosyanın son 0 satırını okuyacak ve ardından daha fazla satırın yazdırılmasını bekleyecektir.
penguin359

16

Yalnızca bir olasılık arıyorsanız ve awkveya kullanmak yerine çoğunlukla kabuğun içinde kalmak perlistiyorsanız, aşağıdakileri yapabilirsiniz:

tail -F /path/to/serverLog | 
grep --line-buffered 'server is up' | 
while read ; do my_command ; done

... my_commandher zaman çalışacak olan " sunucu çalışıyor " günlük dosyasında görünür. Birden çok olasılığı için, belki düşebilir grepve bunun yerine bir kullanmak caseiçinde while.

Başkent , günlük dosyasının döndürülmesini izlemesini -Fsöyler tail; yani eğer mevcut dosya yeniden adlandırılırsa ve aynı isimde başka bir dosya yerini alırsa tail, yeni dosyaya geçer.

--line-bufferedSeçenek söyler grepher satırdan sonra onun tampon temizlemek; Aksi taktirde, my_commandzaman zaman ulaşılamaz (kütüklerin makul boyutlarda çizgileri olduğu varsayılarak).


2
Bu cevabı çok beğendim ama ilk başta benim için işe yaramadı. Bence --line-bufferedseçeneği eklemelisiniz, grepaksi takdirde satırlar arasında çıkışını temizlediğinden emin olun: aksi halde sadece kilitlenir ve my_commandasla ulaşılmaz. İsterseniz ackbir --flushbayrağı vardır; İsterseniz ag, sarmayı deneyin stdbuf. stackoverflow.com/questions/28982518/…
15:40

Bunu kullanarak denedim do exit ;. İyi görünüyordu ama kuyruk hiç bitmedi ve senaryomuz asla bir sonraki satıra geçmedi. Bölümdeki kuyruğu durdurmanın bir yolu var mı do?
Machtyn

14

Bu sorunun cevabı çoktan cevaplanmış gibi görünüyor ama bence daha iyi bir çözüm var.

Daha ziyade tail | whatever, gerçekten istediğin şeyin bence olduğunu düşünüyorum swatch. Swatch, ne istediğinizi yapmak, bir günlük dosyasını izlemek ve eylemleri günlük satırlarına göre yürütmek için özel olarak tasarlanmış bir programdır. tail|fooBunu kullanmak için aktif olarak çalışan bir terminalin olması gerekir. Swatch diğer yandan daemon olarak çalışır ve günlüklerinizi her zaman izler. Swatch tüm Linux dağıtımlarında kullanılabilir.

Denemenizi tavsiye ediyorum. Bir tornavida arka tarafı ile bir çivi çakmak olsa da, olması gerektiği anlamına gelmez.

Renk örneğinde bulabildiğim en iyi 30 saniyelik eğitim burada: http://www.campin.net/newlogcheck.html


1
Bu ders şu an 404 dersi: /
oradan


10

multitailBu işlevselliğin kullanıma hazır olduğu bir programdan kimsenin bahsetmemesi garip . Kullanım örneklerinden biri:

Bir ping komutunun çıktısını göster ve eğer bir zaman aşımı gösteriyorsa, giriş yapmış olan tüm kullanıcılara bir mesaj gönder

multitail -ex timeout "echo timeout | wall" -l "ping 192.168.0.1"

Ayrıca bkz başka örnekler arasında multitailkullanımı.


+1, hiçbir fikrim yoktu `multitail bu tür ninja becerilerini kaçırdı. Bunu gösterdiğin için teşekkürler.
Caleb

8

işi kendi başına yapabilir

Ne kadar basit ve okunabilir olduğunu görelim:

mylog() {
    echo >>/path/to/myscriptLog "$@"
}

while read line;do
    case "$line" in
        *"Printer on fire"* )
            mylog Halting immediately
            shutdown -h now
            ;;
        *DHCPREQUEST* )
            [[ "$line" =~ DHCPREQUEST\ for\ ([^\ ]*)\  ]]
            mylog Incomming or refresh for ${BASH_REMATCH[1]}
            $HOME/SomethingWithNewClient ${BASH_REMATCH[1]}
            ;;
        * )
            mylog "untrapped entry: $line"
            ;;
    esac
  done < <(tail -f /path/to/logfile)

Bash kullanmıyorsanız regex, bu çok hızlı kalabilir!

Fakat + , çok verimli ve ilginç bir tandem

Ancak yüksek yük sunucusu için ve sedçok hızlı ve ölçeklenebilir olduğu için istediğim gibi , bunu sıklıkla kullanırım:

while read event target lost ; do
    case $event in
        NEW )
            ip2int $target intTarget
            ((count[intTarget]++))
        ...

    esac
done < <(tail -f /path/logfile | sed -une '
  s/^.*New incom.*from ip \([0-9.]\+\) .*$/NEW \1/p;
  s/^.*Auth.*ip \([0-9.]\+\) failed./FAIL \1/p;
  ...
')

6

Ben de bunu yapmaya başladım ama onunla daha sofistike oldum. İlgilenmeniz gereken birkaç şey:

  1. Kütüğün kuyruğu zaten "server up" içeriyorsa.
  2. Kuyruk işlemini bulunduktan sonra otomatik olarak sonlandırın.

Bunun boyunca bir şey kullanıyorum:

RELEASE=/tmp/${RANDOM}$$
(
  trap 'false' 1
  trap "rm -f ${RELEASE}" 0
  while ! [ -s ${RELEASE} ]; do sleep 3; done
  # You can put code here if you want to do something
  # once the grep succeeds.
) & wait_pid=$!
tail --pid=${wait_pid} -F /path/to/serverLog \
| sed "1,10d" \
| grep "server is up" > ${RELEASE}

Dosya veri tailiçerene kadar açık tutarak çalışır ${RELEASE}.

grepBaşarılı olduktan sonra :

  1. çıkış yazar ${RELEASE}hangi edecek
  2. ${wait_pid}işlemi sonlandırmak
  3. çıkmak tail

Not: Başlangıçta üretilecek sedsatır sayısını belirlemek tailve bu sayıyı kaldırmak için daha karmaşık olabilir . Ama genel olarak, saat 10.

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.