grep, kediden geçerse EOF'a kadar çıkmaz


19

Bu minimal örnek göz önüne alındığında

( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; )

Bu çıktılar LINE 1ve daha sonra, bir saniye sonra, çıkış LINE 2, beklendiği gibi .


Eğer bunu grep LINE

( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep LINE

davranış, beklendiği gibi önceki durumla aynıdır .


Alternatif olarak, bunu cat

( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | cat

davranış yine beklendiği gibi olur .


Ancak , biz boru eğer grep LINE, ve ardından cat,

( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep LINE | cat

bir saniye geçene kadar çıktı yok ve her iki hat da çıktıda hemen görünüyor, bu da beklemiyordum .


Bu neden oluyor ve son sürümün ilk üç komutla aynı şekilde davranmasını nasıl sağlayabilirim?


catdosyaları birleştirir. İçine geçerek ne yapmaya çalışıyorsunuz cat?
Douglas

15
@DouglasHeld Bağımsız değişkenler olmadan çağrıldığında, catbasitçe okur stdinve çıktı verir stdout. Tabii ki, bu soruyu yerine birçok karmaşık şeyle geldim echove catbunlar önemsiz hale geldi, çünkü sorun çok daha basit örneklerle ortaya çıkıyor.
lisyarus

3
@DouglasHeld: Cat'e borulama genellikle stdout'u bir terminal olmaya zorlamak için yararlıdır. Örneğin, birçok komutu renklendirilmiş çıktı kullanmamak için kolay bir yoldur.
wchargin

Yemin taşma başka bir sorunun bir kopyası olduğunu yemin ederim !
iBug

@wchargin çok teşekkür ederim, bana posix hakkında hiç bilmediğim yeni bir şey öğrettin.
Douglas

Yanıtlar:


38

(En azından GNU) grep'un çıkışı bir terminal olmadığında, çıktısını arabelleğe alır, bu da gördüğünüz davranışa neden olur. Bu kullanarak GNU devre dışı bırakabilirsiniz grep'ın --line-bufferedseçeneği:

( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep --line-buffered LINE | cat

veya stdbufyardımcı program:

( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | stdbuf -oL grep LINE | cat

Borudaki arabelleği kapat bu konuda daha fazla şey var.


26

Basitleştirilmiş açıklama

Birçok yardımcı program gibi, bu bir programa özgü bir şey olmamak grep, standart çıktısını satır arabelleğe alınmış ve tamamen arabelleğe alınmış arasında değiştirir . Önceki durumda, C kütüphanesi, bu verileri tutan tampon dolduruluncaya veya bir satır besleme karakteri eklenene (veya program temiz bir şekilde bitene kadar) bellekte çıktı verilerini tamponlar, bunun üzerine write()tampon içeriğini gerçekten yazmaya çağırır . İkinci durumda, yalnızca bellek içi arabellek doluyor (veya temiz bir şekilde biten program) write().

Daha ayrıntılı açıklama

Bu iyi bilinen ama biraz yanlış bir açıklamadır. Aslında, standart çıktı hat tamponlu değil , GNU C kütüphanesinde ve BSD C kütüphanesinde akıllı tamponludur . Standart girdinin okunması , bellek içi arabelleğini (okuma öncesi girdinin) tükettiğinde de standart çıktı temizlenir ve C kütüphanesi daha fazla girdi almak için çağırmalıdır ve yeni bir satırın başlangıcını okur. (Bunun bir nedeni, başka bir program kendini bir filtrenin her iki ucuna bağladığında ve filtreye yazma ve filtreden okuma arasında dönüşümlü olarak satır satır çalışmayı beklediğinde kilitlenmeyi önlemektir; GNU'daki "yardımcı işlemler" gibi Örneğin.)read()awk

C kütüphanesi etkisi

grepve diğer yardımcı programlar bunu yapar - veya daha doğrusu, kullandıkları C kütüphaneleri bunu yapar, çünkü bu, standart çıktılarının ne olduğunu tespit ettiklerine bağlı olarak C dilinde programlamanın tanımlanmış bir özelliğidir. Etkileşimli bir aygıt değilse (ve yalnızca), tam arabelleğe almayı seçerler, aksi takdirde akıllı arabelleğe almayı seçerler. Bir kanal etkileşimli bir aygıt olarak kabul edilmez, çünkü en azından Unix ve Linux dünyasında etkileşimli bir aygıt olmanın tanımı, esasen isatty()ilgili dosya tanımlayıcı için doğru dönen çağrıdır.

Tam arabelleğe almayı devre dışı bırakmak için geçici çözümler

Gibi bazı yardımcı programlar gördüğünüz gibi yanlış adlandırılmış bu karar değiştirmek grepgibi kendine özgü seçenekleri vardır --line-buffered. Ancak filtre programlarının yok olan küçük bir kısmının gerçekten böyle bir seçeneği var.

Daha genel olarak, C kütüphanesinin belirli iç kısımlarını inceleyen ve karar alma mekanizmasını değiştiren araçlar kullanılabilir (değiştirilecek program set-UID ise ve aynı zamanda belirli C kütüphanelerine özgü ise ve gerçekten de gibi programların C dili üstüne yazılmış veya katmanlı) veya araçlara özgü ptybandageo yok etmek, programın iç görün değiştirmek ancak uçbirimsi standart çıktı olarak kararın "interaktif" olarak çıkar böylece basitçe koyulmasıyla bunu etkiler.

daha fazla okuma


1
Eğer "satır arabelleğe alınmış" ifadesi yanlış bir isim ise, o zaman gerçekten değil grep, temel kütüphane çağrılarının hatası setbuf/setvbuf . C standardı için güvenilir bir çevrimiçi referans bilmiyorum, ancak Linux ve FreeBSD man sayfaları ile birlikte setvbuf"satır arabelleğe alınmış" olarak adlandırılan POSIX açıklaması ile birlikte . Bunun sembolik sabiti bile _IOLBF.
ilkkachu

Şimdi daha iyi öğrendiniz. Bu tamponlama stratejisi , kısa da olsa GNU C kütüphane doco'da açıklanmaktadır. Laurent Bercot bu konuda daha açık sözlü. Ben de bahsettim.
JdeBP

“Beklentiniz yanlış” çıktı tamponlamasının bu mükemmel açıklaması için iyi bir yön olduğunu düşünmemiştim. Umarım kaldırdım ve cevabın her bölümü için bazı açıklayıcı başlıklar ekledim.
Anthony G - Monica için adalet

2
@ilkkachu C standardı gerçekten "satır arabelleğe alınmış" kullanır. Başına 7.21.3 Files , paragraf 3 : Bir akışın tamponsuz olduğunda bir dere tamamen tamponlu zaman bir akım çizgisi tamponlu edildiğinde", ..., ..., karakterler veya bir şekilde ev sahibi ortamından iletilen amaçlanan yeni bir karakterle karşılaşıldığında bloke olur. ... "Aslında C Standardı," satır arabelleğe alınmış "ifadesini tam olarak beş kez kullanır. Yani yanlış isim değil.
Andrew Henle

1
Dahası, burada "akıllı tamponlama" olarak tanımlanan yaklaşım, anladığım kadarıyla, C standardının "satır tamponlama" olarak tanımladığı gibi görünüyor. Özellikle, yeni satırlarda arabelleği temizlemeye ek olarak, "Bir akış satır arabelleğe alındığında, arabelleksiz bir akışta [...] giriş istendiğinde veya karakter, ana bilgisayar ortamından karakterlerin iletilmesini gerektiren bir satır arabelleğe alınmış akışta isteniyor. " Yani bu bir GNU veya BSD tuhaflığı değil, dilin istediği şey.
John Bollinger

7

kullanım

grep --line-buffered

grep'i bir seferde birden fazla satır arabelleklememek için.

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.