Çı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.