Biorąc pod uwagę ten minimalny przykład
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; )
wyprowadza LINE 1
, a następnie po upływie jednej sekundy, wyjścia LINE 2
, jak oczekiwano .
Jeśli to potokujemy grep LINE
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep LINE
zachowanie jest takie samo, jak w poprzednim przypadku, zgodnie z oczekiwaniami .
Jeśli alternatywnie, potokujemy to do cat
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | cat
zachowanie jest ponownie takie samo, jak oczekiwano .
Jeśli jednak potokujemy grep LINE
, a następnie do cat
,
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep LINE | cat
nie ma wyjścia, dopóki nie minie sekunda, a obie linie natychmiast pojawią się na wyjściu, czego się nie spodziewałem .
Dlaczego tak się dzieje i jak mogę sprawić, aby ostatnia wersja zachowywała się tak samo, jak pierwsze trzy polecenia?
cat
łączy pliki. Co próbujesz zrobić, dołączając do niegocat
?cat
po prostu czytastdin
i wysyła dostdout
. Oczywiście wymyśliłem to pytanie z wieloma złożonymi rzeczami w miejsceecho
icat
, ale okazały się one nieistotne, ponieważ problem pojawia się w znacznie prostszych przykładach.Odpowiedzi:
Kiedy
grep
wyjście (przynajmniej GNU) nie jest terminalem, buforuje swoje wyjście, co powoduje zachowanie, które widzisz. Funkcję tę można wyłączyć albo używa GNUgrep
„s--line-buffered
opcję:lub
stdbuf
narzędzie:Wyłącz buforowanie w potoku ma więcej na ten temat.
źródło
Uproszczone wyjaśnienie
Podobnie jak wiele narzędzi, nie jest to coś osobliwego dla jednego programu,
grep
zmienia standardowe wyjście między buforowaniem liniowym a buforowaniem pełnym . W pierwszym przypadku biblioteka C buforuje dane wyjściowe w pamięci, dopóki bufor przechowujący te dane nie zostanie wypełniony lub nie zostanie do niego dodany znak przesunięcia wiersza (lub program zakończy się czysto), po czym wywołujewrite()
zapis zawartości bufora. W tym drugim przypadku tylko bufor w pamięci zapełniający się (lub program kończy się czysto) wyzwalawrite()
.Bardziej szczegółowe wyjaśnienie
To jest dobrze znane, ale nieco błędne wyjaśnienie. W rzeczywistości standardowe wyjście nie jest buforowane liniowo, lecz inteligentnie buforowane w bibliotece GNU C i bibliotece BSD C. Standardowe wyjście jest także zaczerwieniona podczas czytania standardowego wejścia wyczerpuje swój bufor w pamięci (pre-odczytu wejścia) i biblioteka C ma zadzwonić
read()
, aby pobrać trochę więcej wejście i to czyta początek nowej linii. (Jednym z powodów jest zapobieganie zakleszczeniu, gdy inny program łączy się z oboma końcami filtra i oczekuje, że będzie w stanie działać linia po linii, naprzemiennie między zapisem do filtra a odczytem z niego; jak „koprocesowanie” w GNUawk
na przykład.)Wpływ biblioteki C.
grep
i inne narzędzia to robią - lub ściślej mówiąc, biblioteki, których używają, robią to, ponieważ jest to zdefiniowana funkcja programowania w języku C - w oparciu o to, co wykrywają jako standardowe wyjście. Jeśli (i tylko jeśli) nie jest to urządzenie interaktywne, wybierają pełne buforowanie, w przeciwnym razie wybierają inteligentne buforowanie. Potok jest uważany za urządzenie nieinteraktywne, ponieważ definicja bycia urządzeniem interaktywnym, przynajmniej w świecie Unixa i Linuksa, jest w istocieisatty()
wywołaniem zwracającym wartość true dla odpowiedniego deskryptora pliku.Obejścia, aby wyłączyć pełne buforowanie
Niektóre narzędzia, takie jak
grep
takie idiosynkratyczne, jak--line-buffered
ta, zmieniają tę decyzję, która, jak widać, jest źle nazwana. Ale znikomo niewielka część programów filtrujących, których można użyć, faktycznie ma taką opcję.Mówiąc bardziej ogólnie, można użyć narzędzi, które zagłębiają się w określone elementy wewnętrzne biblioteki C i zmieniają jej proces decyzyjny (które mają problemy z bezpieczeństwem, jeśli program, który ma zostać zmieniony, ma ustawiony UID, a także są specyficzne dla poszczególnych bibliotek C, i faktycznie są specyficzne dla programów napisanych lub ułożonych warstwowo na języku C) lub takie narzędzia
ptybandage
, które nie zmieniają wewnętrznych elementów programu, ale po prostu wstawiają pseudo-terminal jako standardowe wyjście, aby decyzja była „interaktywna”, aby wpływać na to.Dalsza lektura
źródło
grep
, ale bazowych wywołań biblioteki,setbuf
/setvbuf
. Nie znam wiarygodnego online odniesienia do standardu C, ale np. Strony podręcznika Linux i FreeBSD wraz z opisem POSIXsetvbuf
nazywają go „buforowanym wierszem”. Nawet stała symboliczna_IOLBF
.Posługiwać się
aby grep nie buforował więcej niż jednej linii na raz.
źródło