Bir dize bulunana kadar bir dosyayı izleme


60

Ben aktif olarak yazılan bir günlük dosyasını izlemek için kuyruk-f kullanıyorum. Günlük dosyasına belirli bir dize yazıldığında, izlemeyi bırakmak ve komut dosyamın kalanına devam etmek istiyorum.

Şu anda kullanıyorum:

tail -f logfile.log | grep -m 1 "Server Started"

Dize bulunduğunda grep beklendiği gibi sonlandırılıyor, ancak betiğin devam edebilmesi için tail komutunun sonlandırılmasını sağlayacak bir yol bulmam gerekiyor.


Orijinal posterin hangi İşletim Sisteminde çalıştığını merak ediyorum. Bir Linux RHEL5 sisteminde, grep komutu eşleşmeyi bulup çıktıktan sonra kuyruk komutunun sadece öldüğünü görünce şaşırdım.
ZaSter

4
@ ZaSter: tailSadece bir sonraki satırda ölür. Bunu deneyin: date > log; tail -f log | grep -m 1 triggerve sonra başka bir kabukta: echo trigger >> logçıktıyı triggerilk kabukta görürsünüz , ancak komutun sonlandırılması yoktur. Sonra şunu deneyin: date >> logikinci kabukta ve ilk kabuktaki komut sonlandırılır. Ama bazen bu çok geç; Tetikleme çizgisi göründükten hemen sonra sonlandırmak istiyoruz, tetikleme çizgisinden sonraki satır tamamlandığında değil
Alfe

Bu mükemmel bir açıklama ve örnek, @ Alfe.
ZaSter


Yanıtlar:


41

Basit bir POSIX tek astar

İşte basit bir astar. Bash'a özgü veya POSIX dışı püf noktalarına veya hatta adlandırılmış bir boruya ihtiyaç duymaz. Gerçekten ihtiyacınız olan fesih ayrıştırmak için ise tailgelen grep. Bu şekilde, bir kez grepsona erdiğinde, komut dosyası tailhenüz bitmediyse bile devam edebilir . Yani bu basit yöntem sizi oraya götürür:

( tail -f -n0 logfile.log & ) | grep -q "Server Started"

grepdizeyi bulana kadar engelleyecektir, bunun üzerine çıkacaktır. Yaparak tailbağımsız çalışır böylece kendi alt kabuğundan koşmak, biz arka planda yerleştirebilirsiniz. Bu arada, ana kabuk, betiğin grepçıkıştan hemen sonra yürütülmesine devam etmekte serbesttir . tailBir sonraki satır logfile yazılana kadar alt kabuğunda oyalanır ve daha sonra çıkar (muhtemelen ana komut dosyası sonlandırıldıktan sonra bile). Ana nokta, boru hattının artık tailsona ermesini beklememesidir, bu yüzden boru hattı en kısa sürede grepçıkar.

Bazı küçük tweaks:

  • -N0 seçeneği, dizginin taillogfile içinde daha önce mevcut olması durumunda mevcut logfile'nin son satırından okumaya başlar.
  • tail-F yerine -F vermek isteyebilirsiniz . POSIX değildir, ancak tailbeklerken günlük döndürülmüş olsa bile çalışmasına izin verir .
  • Seçenek -q yerine -m1 grep, ilk oluşumdan sonra ancak tetikleme çizgisi yazdırılmadan sonlandırılıyor. Ayrıca POSIX, hangi -m1 değil.

3
Bu onaylama tail, arka planda çalışmayı sonsuza dek bırakacaktır . tailArka plandaki alt kabuktaki PID'yi nasıl yakalar ve ana kabuğu nasıl ortaya çıkarırsınız? İsteğe bağlı alt geçici çözümü bulabilirim, oturuma bağlı tüm tailişlemleri öldürerek pkill -s 0 tail.
Rick van der Zwet

1
Çoğu durumda, sorun olmamalıdır. Bunu en başta yapmanızın nedeni, günlük dosyasına daha fazla satır yazmayı beklemenizdir. tailKırık bir boruya yazmaya çalışırken en kısa sürede sona erecektir. Boru greptamamlanır tamamlanmaz kırılacak , böylece bir kez greptamamlandığında, tailgünlük dosyası içinde bir satır daha kaldıktan sonra sona erecektir.
00prometheus

ben bu çözüm kullanıldığında yaptım değil arka plan tail -f.
Trevor Boyd Smith

2
@Trevor Boyd Smith, evet, bu çoğu durumda işe yarar, ancak OP problemi grep'in kuyruk bırakılana kadar tamamlanmaması ve kuyruk grep çıktıktan sonra log dosyasında başka bir satır görünene kadar bırakmaz (kuyruk çalıştığında grep sonuyla kırılan boru hattını besleyin). Bu nedenle, arka plan kuyruğunu almadığınız sürece, komut dosyanız tam olarak yakalanan satır yerine, günlük dosyasında fazladan bir satır görünene kadar yürütmeye devam etmez.
00prometheus

Re "izi [gerekli dize desen] sonra başka bir satır görünene kadar çıkmayacak": Bu çok ustaca ve ben tamamen özlüyorum. Fark etmedim çünkü aradığım desen ortadaydı ve hepsi hızlı bir şekilde basılıyordu. (Yine tanımladığınız davranış çok incedir)
Trevor Boyd Smith

59

Kabul edilen cevap benim için çalışmıyor, ayrıca kafa karıştırıcı ve günlük dosyasını değiştiriyor.

Böyle bir şey kullanıyorum:

tail -f logfile.log | while read LOGLINE
do
   [[ "${LOGLINE}" == *"Server Started"* ]] && pkill -P $$ tail
done

Günlük çizgisi modelle eşleşiyorsa, tailbu betiğin başlamasını öldürün .

Not: Çıktıyı ekranda da görüntülemek istiyorsanız | tee /dev/tty, while döngüsünde test etmeden önce çizgiyi ya da ekoyu kullanın.


2
Bu çalışır, ancak pkillPOSIX tarafından belirtilmez ve her yerde kullanılamaz.
Richard Hansen,

2
Bir süre döngüsüne ihtiyacınız yok. -g seçeneğiyle saati kullanın ve kendinizi kötü pkill komutundan koruyabilirsiniz.
l1zard

@ l1zard Onu çıkarabilir misiniz? Belirli bir satır görünene kadar bir günlük dosyasının kuyruğunu nasıl izlersiniz? (Daha az önemlisi, ama -g eklendiğinde merak ediyorum; bu seçeneğe sahip daha yeni bir Debian sunucum ve onsuz başka bir eski RHEL tabanlı bir sunucu var).
Rob Whelan

Kuyruğun neden burada gerekli olduğu bile bana göre belli değil. Bu doğru anladığım kadarıyla, kullanıcı bir günlük dosyasındaki belirli bir anahtar kelime göründüğünde belirli bir komutu çalıştırmak istiyor. Aşağıda saatini kullanarak verilen komut bu işi yapar.
l1zard

Tam olarak değil - belirli bir dize günlük dosyasına eklendiğinde denetleniyor . Tomcat veya JBoss'un tam olarak ne zaman başlatıldığını kontrol etmek için bunu kullanıyorum; her seferinde "Sunucu başlatıldı" (veya benzeri) yazarlar.
Rob Whelan

16

Bash kullanıyorsanız (en azından, ancak POSIX tarafından tanımlanmamış gibi görünüyor, bu nedenle bazı kabuklarda eksik olabilir), sözdizimini kullanabilirsiniz.

grep -m 1 "Server Started" <(tail -f logfile.log)

Daha önce bahsettiğimiz FIFO çözümlerine çok benziyor ancak yazması çok daha kolay.


1
Bu işe SIGTERM
yarar

3
@mems, günlük dosyasındaki herhangi bir ek satırı yapacak. tail, Okumak çıkışa denemek ve sonra sonlandıracaktır bir SIGPIPE alacaksınız. Yani prensipte haklısın; Bir taildaha hiçbir zaman günlük dosyasına bir şey yazılmamışsa bu sürebilir. Uygulamada bu birçok insan için çok temiz bir çözüm olabilir.
Alfe

14

Çıkmanın birkaç yolu vardır tail:

Kötü Yaklaşım: tailBaşka bir satır yazmaya zorla

Bir eşleşme bulduktan ve çıktıktan tailhemen sonra başka bir çıktı satırı yazmaya zorlayabilirsiniz grep. Bu , çıkmasına neden tailolan bir elde etmeye neden SIGPIPEolur. Bunu yapmanın bir yolu, çıkışların tailardından izlenen dosyayı değiştirmektir grep.

İşte bazı örnek kod:

tail -f logfile.log | grep -m 1 "Server Started" | { cat; echo >>logfile.log; }

Bu örnekte, catstdout'unu kapatana kadar grepçıkmaz, bu nedenle stdin'i kapatma şansına sahip tailolmadan önce boruya yazı yazması mümkün değildir grep. değiştirilmemiş catstandart çıktının çoğaltılması için kullanılır grep.

Bu yaklaşım nispeten basittir, ancak birkaç dezavantajı vardır:

  • Eğer grepkapanır Stdin kapatmadan önce stdout'a, her zaman bir yarış durumu olacaktır: greptetikler stdout'u kapanır cattetikler çıkmak için echotetikleme, tailçıkış için bir çizgi. Bu satır daha grepönce gönderilirse grepstdin'i kapatma şansı oldu, başka bir satır yazana kadar tailelde edemedi SIGPIPE.
  • Günlük dosyasına yazma erişimi gerektirir.
  • Günlük dosyasını değiştirerek Tamam olmanız gerekir.
  • Başka bir işlemle aynı anda yazıyorsanız, günlük dosyasını bozabilirsiniz (yazma işlemi bir araya getirilebilir, böylece bir günlük iletisinin ortasında yeni bir satır görünmesine neden olabilir).
  • Bu yaklaşım kendine özgüdür tail- diğer programlarla çalışmaz.
  • Üçüncü boru hattı aşaması (Aşağıdaki gibi bir POSIX uzantısı kullandığınız sürece zor ikinci boru hattı aşamasının dönüş koduna erişmek için yapar bash'ın PIPESTATUSdizisi). Bu, bu durumda büyük bir sorun değildir, çünkü grepher zaman 0 döndürür, ancak genel olarak orta aşama, dönüş kodunu umursadığınız farklı bir komutla değiştirilebilir (örneğin, "sunucu başlatıldığında" algılandığında 0 döndüren bir şey, 1 "sunucu başlatılamadı" algılandığında).

Bir sonraki yaklaşımlar bu kısıtlamaları önler.

Daha İyi Bir Yaklaşım: Boru Hattlarından Kaçının

Boru hattının tamamen önlenmesi için FIFO'yu kullanabilirsiniz, bu işlem yürütmenin bir kez grepgeri dönmesine izin verir . Örneğin:

fifo=/tmp/tmpfifo.$$
mkfifo "${fifo}" || exit 1
tail -f logfile.log >${fifo} &
tailpid=$! # optional
grep -m 1 "Server Started" "${fifo}"
kill "${tailpid}" # optional
rm "${fifo}"

Yorum ile işaretlenmiş satırlar # optionalkaldırılabilir ve program çalışmaya devam eder; tailbaşka bir girdi satırı okuyana veya başka bir işlem tarafından öldürülünceye kadar oyalanacaktır.

Bu yaklaşımın avantajları:

  • günlük dosyasını değiştirmeniz gerekmez
  • yaklaşım, diğer kamu kurumları için de işe yarıyor tail
  • yarış koşullarından muzdarip değil
  • Kolayca geri dönüş değerini grep(veya kullandığınız alternatif komut ne olursa olsun) alabilirsiniz

Bu yaklaşımın dezavantajı, özellikle FIFO'yu yönetmek karmaşıklıktır: Geçici bir dosya adı güvenli bir şekilde oluşturmanız gerekir ve kullanıcı, Ctrl-C'nin ortasına bassa bile geçici FIFO'nun silinmesini sağlamalısınız. senaryo. Bu bir tuzak kullanılarak yapılabilir.

Alternatif Yaklaşım: Öldürmek İçin Mesaj Gönder tail

Bunun tailgibi bir sinyal göndererek boru hattı aşamasından çıkabilirsiniz SIGTERM. Zorluk, aynı yerde iki şeyi güvenilir bir şekilde kodda bilmektir: tail'nin PID'si ve çıkıp çıkmadığı grep.

Gibi bir boru hattı tail -f ... | grep ..., tailarka planda tailve okuyarak bir değişken PID kaydetmek için ilk boru hattı aşamasını değiştirmek kolaydır $!. Bu çalıştırmak için ikinci boru hattı aşamasını değiştirmek için de kolaydır killzaman grepçıkar. Sorun şu ki, boru hattının iki aşaması ayrı "uygulama ortamlarında" (POSIX standardının terminolojisinde) çalışıyor, böylece ikinci boru hattı aşaması, birinci boru hattı aşaması tarafından belirlenen herhangi bir değişkeni okuyamıyor. Kabuk değişkenleri kullanmadan, ya ikinci aşama bir şekilde geri dönüşünü tailöldürebilmesi için tailPID'sini grepbulmalı ya da ilk aşama grepgeri döndüğünde bildirilmelidir .

İkinci aşama, PID'sini pgrepalmak için kullanılabilir tail, ancak bu güvenilir olmaz (yanlış işlemle karşılaşabilirsiniz) ve taşınabilir pgrepdeğil ( POSIX standardı tarafından belirtilmez).

İlk aşama, PID'yi boru vasıtasıyla PID'yi ikinci aşamaya gönderebilir echo, ancak bu dizi tailçıktısı ile karışır . İkiye ayrılmadan kaldırma, çıktısına bağlı olarak karmaşık bir kaçış şeması gerektirebilir tail.

İkinci boru hattı aşamasının grepçıkarken ilk boru hattı aşamasını bildirmesi için bir FIFO kullanabilirsiniz . O zaman ilk aşamada öldürebilir tail. İşte bazı örnek kod:

fifo=/tmp/notifyfifo.$$
mkfifo "${fifo}" || exit 1
{
    # run tail in the background so that the shell can
    # kill tail when notified that grep has exited
    tail -f logfile.log &
    # remember tail's PID
    tailpid=$!
    # wait for notification that grep has exited
    read foo <${fifo}
    # grep has exited, time to go
    kill "${tailpid}"
} | {
    grep -m 1 "Server Started"
    # notify the first pipeline stage that grep is done
    echo >${fifo}
}
# clean up
rm "${fifo}"

Bu yaklaşım, daha karmaşık olması dışında, önceki yaklaşımın tüm avantajlarına ve dezavantajlarına sahiptir.

Arabelleğe Alma Hakkında Bir Uyarı

POSIX, stdin ve stdout akışlarının tamamen arabelleğe alınmasına izin verir; bu, tailçıktısının grepisteğe bağlı olarak uzun bir süre tarafından işlenemeyeceği anlamına gelir . GNU sistemlerinde herhangi bir sorun olmamalıdır: Tüm tamponlamayı engelleyen GNU grepkullanır read()ve GNU stdout'a yazarken tail -fdüzenli çağrılar yapar fflush(). GNU dışı sistemler, tamponları devre dışı bırakmak veya düzenli olarak temizlemek için özel bir şey yapmak zorunda kalabilir.


Çözüm (diğerleri gibi, sizi suçlamam), izleme başlamadan önce günlük dosyasına zaten yazılmış olanları özleyecektir. Bu tail -fsadece son on satırın ardından da aşağıdakilerin çıktısını alır. Bunu iyileştirmek -n 10000için kuyruğa seçeneği ekleyebilirsiniz, böylece son 10000 satırlar da verilir.
Alfe

Başka bir fikir: Sizin fifo çözüm çıktısını ileterek, bence, doğruldu edilebilir tail -ffifo'ya ve üzerinde grepping: mkfifo f; tail -f log > f & tailpid=$! ; grep -m 1 trigger f; kill $tailpid; rm f.
Alfe

@Alfe: Yanılıyor olabilirim, ancak tail -f logbir FIFO'ya yazmanın bazı sistemlerin (örneğin, GNU / Linux) satır tabanlı tamponlama yerine blok tabanlı tamponlama kullanmasına neden olacağına inanıyorum grep. İşlem kaydında görünür. Sistem stdbuf, GNU coreutils gibi tamponlamayı değiştirmek için bir yardımcı program sağlayabilir . Bununla birlikte, böyle bir yardımcı program taşınabilir değildir.
Richard Hansen,

1
@Alfe: Aslında, POSIX bir terminalle etkileşime girme dışında tamponlama hakkında hiçbir şey söylemiyor gibi görünüyor, bu nedenle standartlar açısından basit bir çözümün benim karmaşık çözümüm kadar iyi olduğunu düşünüyorum. Bununla birlikte, çeşitli uygulamaların her durumda gerçekte nasıl davrandıklarından% 100 emin değilim.
Richard Hansen

Aslında, şimdi grep -q -m 1 trigger <(tail -f log)başka bir yerde önerilen daha basit tailolanı kullanıyorum ve arka planda bir satır daha gerekenden daha uzun sürdüğü gerçeğiyle yaşıyorum .
Alfe

9

@ 00prometheus cevabında genişleyeyim (ki en iyisi).

Belki de süresiz olarak beklemek yerine zaman aşımı kullanmalısınız.

Aşağıdaki bash işlevi, verilen arama terimi görünene veya belirli bir zaman aşımına ulaşılana kadar engeller.

Dize zaman aşımı içinde bulunursa çıkış durumu 0 olacaktır.

wait_str() {
  local file="$1"; shift
  local search_term="$1"; shift
  local wait_time="${1:-5m}"; shift # 5 minutes as default timeout

  (timeout $wait_time tail -F -n0 "$file" &) | grep -q "$search_term" && return 0

  echo "Timeout of $wait_time reached. Unable to find '$search_term' in '$file'"
  return 1
}

Belki de günlük dosyası henüz sunucunuzu başlattıktan sonra mevcut değildir. Bu durumda, dizeyi aramadan önce görünmesini beklemelisiniz:

wait_server() {
  echo "Waiting for server..."
  local server_log="$1"; shift
  local wait_time="$1"; shift

  wait_file "$server_log" 10 || { echo "Server log file missing: '$server_log'"; return 1; }

  wait_str "$server_log" "Server Started" "$wait_time"
}

wait_file() {
  local file="$1"; shift
  local wait_seconds="${1:-10}"; shift # 10 seconds as default timeout

  until test $((wait_seconds--)) -eq 0 -o -f "$file" ; do sleep 1; done

  ((++wait_seconds))
}

İşte nasıl kullanabilirsiniz:

wait_server "/var/log/server.log" 5m && \
echo -e "\n-------------------------- Server READY --------------------------\n"

Peki, timeoutemir nerede ?
ayanamist

Aslında, kullanma timeoutözelliği, başlayamayan ve zaten çıkmış bir sunucuyu beklemeden süresiz olarak beklemenin tek güvenilir yoludur.
gluk47,

1
Bu cevap en iyisidir. Sadece fonksiyonu kopyalayın ve çağırın, çok kolay ve tekrar kullanılabilir
Hristo Vrigazov

6

Bu yüzden bazı testler yaptıktan sonra, bu işi yapmanın hızlı bir yolunu buldum. Görünüşe göre kuyruk-grep bırakıldığında bırakacaktır, ancak bir mandal var. Yalnızca dosya açılıp kapatıldığında tetiklenir gibi görünür. Grep eşleşmeyi bulduğunda boş dizeyi dosyaya ekleyerek bunu başardım.

tail -f logfile |grep -m 1 "Server Started" | xargs echo "" >> logfile \;

Dosyanın açılıp kapanmasının neden borunun kapalı olduğunu anlamak için kuyruğu tetiklediğinden emin değilim, bu yüzden bu davranışa güvenemem. ama şimdilik çalışıyor gibi görünüyor.

Kapatılma nedeni, -F bayrağına karşı -F bayrağına bakın.


1
Bu işe yarar, çünkü logfile eklenmesi tailbaşka bir satır çıkmasına neden olur , fakat o zamana grepkadar çıkılmıştır (muhtemelen - orada bir yarış durumu vardır). Eğer grepzaman çıkıldı tailbaşka bir hat yazıyor, tailbir alacak SIGPIPE. Bu tailhemen çıkmak için neden olur .
Richard Hansen,

1
Bu yaklaşımın dezavantajları: (1) bir yarış durumu var (her zaman hemen çıkmayabilir) (2) günlük dosyasına yazma erişimi gerektirir (3) günlük dosyasını (4) değiştirirken sorun gidermeniz gerekebilir (4) günlük dosyası (5) sadece işe yarar tail(6), farklı dize eşleşmelerine ("sunucu başlatıldı" vs. "sunucu başlatılamadı") bağlı olarak farklı davranması için kolayca değişiklik yapamazsınız çünkü iade kodunu kolayca alamazsınız. Boru hattının orta aşaması. Tüm bu sorunlardan kaçınan alternatif bir yaklaşım var - cevabımı görün.
Richard Hansen,

6

Hali hazırda belirtildiği gibi, buradaki tail -fçözümlerin tümü, daha önce günlüğe kaydedilen bir "Sunucu Başlatıldı" satırını alma riski taşır (bu, kendi durumunuzda günlüğe kaydedilen satır sayısına ve günlük dosyasının dönüşüne bağlı olarak bir sorun olabilir veya olmayabilir) / kesme).

Aşırı karmaşık şeylerden ziyade tail, bmike bir perl snippit ile gösterdiği gibi , sadece daha akıllıca kullanın . En basit çözüm, başlatma ve durdurma koşul desenleriyle retailentegre regex desteğine sahip olan şudur :

retail -f -u "Server Started" server.log > /dev/null

Bu, dizenin tail -filk yeni örneği görünene kadar dosyayı normal bir şekilde izler , sonra çıkar. (Bu -useçenek normal "follow" modundayken dosyanın son 10 satırındaki mevcut satırlarda tetiklenmez.)


GNU kullanıyorsanız tail( coreutils'den ), bir sonraki en basit seçenek kullanmak --pidve bir FIFO (adlandırılmış yöneltme):

mkfifo ${FIFO:=serverlog.fifo.$$}
grep -q -m 1 "Server Started" ${FIFO}  &
tail -n 0 -f server.log  --pid $! >> ${FIFO}
rm ${FIFO}

Bir FIFO kullanılır, çünkü bir PID almak ve geçmek için işlemlerin ayrı ayrı başlatılması gerekir. Bir FIFO hala neden zamanında yazmak için takılmak aynı sorunu muzdarip tailalmak için bir SIGPIPE , kullanmak --pid, böylece seçeneği tailo fark ettiğinde çıkışlar grepsonlandırdı (geleneksel olarak izlemek için kullanılan yazar ziyade süreç okuyucu , ancak tailkötü kokan gerçekten umurumda değil). Seçenek -n 0, taileski çizgilerin bir eşleşmeyi tetiklememesi için kullanılır.


Sonunda, durum bilgisi olan bir kuyruk kullanabilirsiniz , bu geçerli dosya ofsetini kaydeder, böylece sonraki çağrılar yalnızca yeni satırlar gösterir (dosya dönmesini de idare eder). Bu örnekte eski FWTK retail* kullanılmaktadır:

retail "${LOGFILE:=server.log}" > /dev/null   # skip over current content
while true; do
    [ "${LOGFILE}" -nt ".${LOGFILE}.off" ] && 
       retail "${LOGFILE}" | grep -q "Server Started" && break
    sleep 2
done

* Not, aynı ad, önceki seçeneğe göre farklı bir program.

CPU-hogging loop'a sahip olmaktansa, dosyanın zaman damgasını state dosyası ( .${LOGFILE}.off) ile karşılaştırın ve uykuya bırakın. Gerekirse -Tdurum dosyasının konumunu belirlemek için " " kullanın , yukarıdakiler geçerli dizini alır. Bu koşulu atlamaktan çekinmeyin veya Linux'ta inotifywaitbunun yerine daha verimli kullanabilirsiniz :

retail "${LOGFILE:=server.log}" > /dev/null
while true; do
    inotifywait -qq "${LOGFILE}" && 
       retail "${LOGFILE}" | grep -q "Server Started" && break
done

retail"120 saniye geçtiyse ve perakende satış hala satırı okumadıysa, bir hata kodu verin ve satıştan çıkın" gibi bir zaman aşımı ile birleştirebilir miyim ?
kiltek,

@kiltek GNU'yu timeout(coreutils) başlatmak retailve zaman aşımına uğramış çıkış kodunu (124) kontrol etmek için timeoutkullanın (ayarladığınız süreden sonra başlatmak için kullandığınız komutu öldürür)
mr.spuratic

4

İşlem kontrolü ve sinyalizasyona girmeniz gerekeceğinden bu biraz zor olacaktır. Daha fazla kludgey, PID izlemeyi kullanan iki kodlu bir çözüm olacaktır. Bunun gibi adlandırılmış yöneltmeler kullanmak daha iyi olurdu .

Hangi kabuk betiğini kullanıyorsun?

Hızlı ve kirli bir komut dosyası çözümü için - File: Tail kullanarak perl komut dosyası hazırlarım

use File::Tail;
$file=File::Tail->new(name=>$name, maxinterval=>300, adjustafter=>7);
while (defined($line=$file->read)) {
    last if $line =~ /Server started/;
}

Bu süre zarfında yazdırmak yerine, dizenin eşleşmesini filtreleyebilir ve komut dosyanızın devam etmesini sağlamak için süre döngüsünden çıkabilirsiniz.

Bunlardan herhangi biri, aradığınız izleme akışı kontrolünü uygulamak için küçük bir öğrenmeyi içermelidir.


bash kullanarak. perl-fu'm o kadar güçlü değil, ama buna bir şans vereceğim.
Alex Hofsteede

Borular kullanın - bash'ı severler ve bash onları sever. (ve yedekleme yazılımınız borularınızdan birine çarptığında size saygı
duyacaktır

maxinterval=>300her beş dakikada bir dosyayı kontrol edeceği anlamına gelir. Hattımın bir anda dosyada görüneceğini bildiğim için, çok daha agresif bir anket kullanıyorum:maxinterval=>0.2, adjustafter=>10000
Stephen Ostermiller 09:14

2

dosyanın görünmesini bekle

while [ ! -f /path/to/the.file ] 
do sleep 2; done

dize dosyasında apper için bekleyin

while ! grep "the line you're searching for" /path/to/the.file  
do sleep 10; done

https://superuser.com/a/743693/129669


2
Bu yoklamanın iki ana dezavantajı vardır: 1. Kayıt işleminden tekrar tekrar geçerek hesaplama süresini boşa harcar. /path/to/the.file1,4GB büyüklüğünde olanı düşünün ; o zaman bunun bir sorun olduğu açık. 2. Kayıt girişi göründüğünde, en kötü durumda 10 saniye boyunca gerekenden daha uzun süre bekler.
Alfe

2

Bundan daha temiz bir çözüm düşünemiyorum:

#!/usr/bin/env bash
# file : untail.sh
# usage: untail.sh logfile.log "Server Started"
(echo $BASHPID; tail -f $1) | while read LINE ; do
    if [ -z $TPID ]; then
        TPID=$LINE # the first line is used to store the previous subshell PID
    else
        echo "$LINE"; [[ "$LINE" == *"${*:2}"* ]] && kill -3 $TPID && break
    fi
done

tamam, belki de isim iyileştirmelere tabi olabilir ...

Avantajları:

  • özel bir yardımcı program kullanmaz
  • diske yazmıyor
  • incelikle kuyruğundan çıkar ve boruyu kapatır
  • oldukça kısa ve anlaşılması kolay

2

Bunu yapmak için kuyruğa gerek yok. Bence izle emri ne aradığınızı. Watch komutu bir dosyanın çıkışını izler ve çıkış değiştiğinde -g seçeneğiyle sonlandırılabilir .

watch -g grep -m 1 "Server Started" logfile.log && Yournextaction

1
Bu iki saniyede bir kez çalıştığından, satır günlük dosyasında göründüğünde hemen çıkmaz. Ayrıca, günlük dosyası çok büyükse, iyi çalışmaz.
Richard Hansen,


1

Alex, bence bu sana çok yardımcı olacak.

tail -f logfile |grep -m 1 "Server Started" | xargs echo "" >> /dev/null ;

bu komut hiçbir zaman günlük dosyasına bir giriş yapmaz, ancak sessizce grep ...


1
Bu işe yaramaz - logfileaksi halde eklemek zorundasınız, aksi takdirde tailbaşka bir hat çıkarıp grepölmüş olduğunu tespit etmeden önce keyfi bir şekilde uzun zaman alabilir SIGPIPE.
Richard Hansen,

1

Aşağıda, bazı durumlarda çok tehlikeli ve hatta imkansız olan log dosyasına yazmanızı gerektirmeyen çok daha iyi bir çözüm var.

sh -c 'tail -n +0 -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'

Şu anda sadece bir yan etkiye sahip, tailişlem bir sonraki satır günlüğe yazılana kadar arka planda kalacaktır.


tail -n +0 -fDosyanın başından başlar. tail -n 0 -fDosyanın sonundan başlar.
Stephen Ostermiller

1
myscript.sh: line 14: 7845 Terminated sh -c 'tail...
Aldığım

Bu cevabın "sıradaki listesinin" "sıradaki" olması gerektiğine inanıyorum.
Stephen Ostermiller

Bu çalışır, ancak tailarka planda çalışan bir işlem kalır.
cbaldan

1

Buradaki diğer çözümlerin birkaç sorunu var:

  • Eğer kayıt işlemi zaten kapalıysa veya döngü sırasında azalırsa, süresiz olarak çalışacaktır.
  • yalnızca görüntülenmesi gereken bir günlüğü düzenleme
  • gereksiz yere ek bir dosya yazmak
  • ek mantığa izin vermemek

İşte bir örnek olarak tomcat kullanarak geldiğim şey:

function startTomcat {
    loggingProcessStartCommand="${CATALINA_HOME}/bin/startup.sh"
    loggingProcessOwner="root"
    loggingProcessCommandLinePattern="${JAVA_HOME}"
    logSearchString="org.apache.catalina.startup.Catalina.start Server startup"
    logFile="${CATALINA_BASE}/log/catalina.out"

    lineNumber="$(( $(wc -l "${logFile}" | awk '{print $1}') + 1 ))"
    ${loggingProcessStartCommand}
    while [[ -z "$(sed -n "${lineNumber}p" "${logFile}" | grep "${logSearchString}")" ]]; do
        [[ -z "$(ps -ef | grep "^${loggingProcessOwner} .* ${loggingProcessCommandLinePattern}" | grep -v grep)" ]] && { echo "[ERROR] Tomcat failed to start"; return 1; }
        [[ $(wc -l "${logFile}" | awk '{print $1}') -lt ${lineNumber} ]] && continue
        #sed -n "${lineNumber}p" "${logFile}"
        let lineNumber++
    done
    #sed -n "${lineNumber}p" "${logFile}"
    echo "[INFO] Tomcat has started"
}

1

tailKomut backgrounded ve pid ile yankılandı edilebilir grepalt kabuğa. Alt grepkabukta, EXIT üzerindeki bir tuzak işleyicisi tailkomutu öldürebilir .

( (sleep 1; exec tail -f logfile.log) & echo $! ; wait ) | 
     (trap 'kill "$pid"' EXIT; pid="$(head -1)"; grep -m 1 "Server Started")

1

Hepsini oku. tldr: kuyruğun sonlandırılmasını grep'ten ayırın.

En uygun iki form

( tail -f logfile.log & ) | grep -q "Server Started"

ve eğer bash varsa

grep -m 1 "Server Started" <(tail -f logfile.log)

Fakat arka planda oturan kuyruk sizi rahatsız ediyorsa, burada bir beşten veya başka herhangi bir cevaptan daha güzel bir yol var. Bash gerektirir.

coproc grep -m 1 "Server Started"
tail -F /tmp/x --pid $COPROC_PID >&${COPROC[1]}

Ya da bir şeyleri çıkaran kuyruk değilse,

coproc command that outputs
grep -m 1 "Sever Started" ${COPROC[0]}
kill $COPROC_PID

0

İnotify kullanmaya çalışın (inotifywait)

Herhangi bir dosya değişikliği için inotifywait'i ayarlayın, sonra grep ile dosyayı kontrol edin, eğer sadece inotifywait'i tekrar bulunmadıysa, döngüden çıkarsanız ...


Bu şekilde, bir dosyanın her yazışında dosyanın tamamı kontrol edilmelidir. Günlük dosyaları için iyi çalışmıyor.
Grawity

1
Başka bir yol iki script yapmaktır: 1. tail -f logfile.log | grep -m 1 "Sunucu Başlatıldı"> / / tmp / found 2. firstscript.sh & MYPID = $!; inotifywait -e MODIFY / tmp / found; öldür -KILL - $ MYPID
Evengard

PID'yi yakalamayı ve sonra inotifywait'i kullanmaya çalıştığınızı göstermek için cevabınızı düzenlemeyi çok isterim - eskiden aşırdığı ancak daha sofistike bir araca ihtiyaç duyan biri için kavraması kolay olan zarif bir çözüm.
bmike

Yakalamak istediğiniz şeyin bir PID'i? Ne istersen biraz daha fazla
açıklarsan başarabilirim

0

Satır yazıldığı anda ayrılmak istiyorsunuz, ancak bir zaman aşımından sonra da ayrılmak istiyorsunuz:

if (timeout 15s tail -F -n0 "stdout.log" &) | grep -q "The string that says the startup is successful" ; then
    echo "Application started with success."
else
    echo "Startup failed."
    tail stderr.log stdout.log
    exit 1
fi

-2

buna ne dersin:

doğru olsa da; yapabiliyorsan [ ! -z $ (grep "myRegEx" myLog.log)]; sonra kırmak; fi; tamam

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.