Çıkmanın birkaç yolu vardır tail
:
Kötü Yaklaşım: tail
Başka bir satır yazmaya zorla
Bir eşleşme bulduktan ve çıktıktan tail
hemen sonra başka bir çıktı satırı yazmaya zorlayabilirsiniz grep
. Bu , çıkmasına neden tail
olan bir elde etmeye neden SIGPIPE
olur. Bunu yapmanın bir yolu, çıkışların tail
ardı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, cat
stdout'unu kapatana kadar grep
çıkmaz, bu nedenle stdin'i kapatma şansına sahip tail
olmadan önce boruya yazı yazması mümkün değildir grep
. değiştirilmemiş cat
standart çıktının çoğaltılması için kullanılır grep
.
Bu yaklaşım nispeten basittir, ancak birkaç dezavantajı vardır:
- Eğer
grep
kapanır Stdin kapatmadan önce stdout'a, her zaman bir yarış durumu olacaktır: grep
tetikler stdout'u kapanır cat
tetikler çıkmak için echo
tetikleme, tail
çıkış için bir çizgi. Bu satır daha grep
önce gönderilirse grep
stdin'i kapatma şansı oldu, başka bir satır yazana kadar tail
elde 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 PIPESTATUS
dizisi). Bu, bu durumda büyük bir sorun değildir, çünkü grep
her 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 grep
geri 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 # optional
kaldırılabilir ve program çalışmaya devam eder; tail
baş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 tail
gibi 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 ...
, tail
arka planda tail
ve 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 kill
zaman 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 tail
PID'sini grep
bulmalı ya da ilk aşama grep
geri döndüğünde bildirilmelidir .
İkinci aşama, PID'sini pgrep
almak için kullanılabilir tail
, ancak bu güvenilir olmaz (yanlış işlemle karşılaşabilirsiniz) ve taşınabilir pgrep
değ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 grep
isteğ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 grep
kullanır read()
ve GNU stdout'a yazarken tail -f
dü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.