Nie rozumiem, jak przepływają dane w rurociągu, i mam nadzieję, że ktoś może wyjaśnić, co się tam dzieje.
Myślałem, że potok poleceń przetwarza pliki (tekst, tablice ciągów) wiersz po wierszu. (Jeśli każde polecenie działa osobno wiersz po wierszu.) Każdy wiersz tekstu przechodzi przez potok, polecenia nie czekają, aż poprzednie zakończy przetwarzanie całego tekstu.
Ale wydaje się, że tak nie jest.
Oto przykładowy test. Jest kilka wierszy tekstu. Wielkie litery i powtarzam każdą linię dwa razy. Robię to z cat text | tr '[:lower:]' '[:upper:]' | sed 'p'
.
Aby śledzić proces, możemy go uruchomić „interaktywnie” - pomiń wejściową nazwę pliku cat
. Każda część rurociągu przebiega linia po linii:
$ cat | tr '[:lower:]' '[:upper:]'
alkjsd
ALKJSD
sdkj
SDKJ
$ cat | sed 'p'
line1
line1
line1
line 2
line 2
line 2
Ale cały potok czeka, aż skończę dane wejściowe EOF
i dopiero wtedy wypisuje wynik:
$ cat | tr '[:lower:]' '[:upper:]' | sed 'p'
I am writing...
keep writing...
now ctrl-D
I AM WRITING...
I AM WRITING...
KEEP WRITING...
KEEP WRITING...
NOW CTRL-D
NOW CTRL-D
Czy tak ma być? Dlaczego nie jest wiersz po wierszu?
cat
buforuje się aż do zamknięcia standardowego wejścia.tr
ised
wykonaj linie przetwarzaniacat
sprzed zamknięcia stdinOdpowiedzi:
Istnieje ogólna reguła buforowania, po której następuje standardowa biblioteka C we / wy (
stdio
) używana przez większość programów uniksowych. Jeśli wyjście trafia do terminala, jest opróżniane na końcu każdej linii; w przeciwnym razie jest opróżniany tylko wtedy, gdy bufor (8 KB na moim systemie Linux / amd64; może być inny na twoim) jest pełny.Jeśli wszystkie media były zgodnie z ogólną regułę, że widzisz wyjścia z opóźnieniem wszystkich przykładów (
cat|sed
,cat|tr
, icat|tr|sed
). Ale jest wyjątek: GNUcat
nigdy nie buforuje swoich danych wyjściowych. Nie używastdio
lub zmienia domyślnąstdio
zasadę buforowania.Mogę być całkiem pewien, że używasz GNU,
cat
a nie jakiegoś innego Uniksa,cat
ponieważ inni nie zachowaliby się w ten sposób. Tradycyjny unixcat
ma-u
opcję żądania niebuforowanych danych wyjściowych. GNUcat
ignoruje tę-u
opcję, ponieważ jej dane wyjściowe są zawsze niebuforowane.Tak więc, gdy masz rurkę z
cat
lewym, w systemie GNU, przepływ danych przez rurę nie będzie opóźniony.cat
Nawet nie będzie wiersz po wierszu - terminal robi to. Podczas wpisywania danych wejściowych dla cat, twój terminal jest w trybie „kanonicznym” - oparty na linii, z klawiszami edycji takimi jak backspace i ctrl-U, oferującymi możliwość edycji linii, którą wpisałeś przed wysłaniem Enter.W tym
cat|tr|sed
przykładzietr
nadal odbiera dane,cat
gdy tylko naciśniesz Enter, aletr
postępuje zgodnie zstdio
domyślną zasadą: jego wyjście przechodzi do potoku, więc nie opróżnia się po każdej linii. Zapisuje do drugiego potoku, gdy bufor jest pełny lub po otrzymaniu EOF, w zależności od tego, co nastąpi wcześniej.sed
postępuje również zgodnie zstdio
domyślną polityką, ale jego dane wyjściowe trafiają do terminala, więc zapisze każdą linię, gdy tylko się z nią skończy. Wpływa to na to, ile musisz wpisać, zanim coś pojawi się na drugim końcu potoku - jeślised
buforowanie blokowe jego danych wyjściowych, musisz wpisać dwa razy więcej (aby wypełnićtr
bufor wyjściowy ised
dane wyjściowe bufor).GNU
sed
ma-u
opcję, więc jeśli odwrócisz kolejność icat|sed -u|tr
użyjesz, zobaczysz, że dane wyjściowe pojawią się ponownie. (Tased -u
opcja może być dostępna gdzie indziej, ale nie sądzę, że jest to starożytna tradycja uniksowa taka jakcat -u
). O ile wiem, nie ma równoważnej opcjitr
.Istnieje narzędzie o nazwie,
stdbuf
które pozwala zmienić tryb buforowania każdego polecenia, które korzysta zstdio
wartości domyślnych. Jest to trochę kruche, ponieważ wykorzystuje sięLD_PRELOAD
do osiągnięcia czegoś, co biblioteka C nie została zaprojektowana do obsługi, ale w tym przypadku wydaje się działać:źródło
tee
add
także zwykle grać według własnych zasad. Po połączeniu w wyobraźni trzy narzędzia mogą w przenośny sposób zaprzeczyć jakiejkolwiek potrzebiestdbuf
w rurociągach w tle.Właściwie zajęło mi to trochę zrozumienia, a jeszcze więcej odpowiedzi. Świetne pytanie (będę go głosować w następnej kolejności).
Zaniedbałeś próby wypróbowania
tr | sed
powyższych elementów do debugowania:Więc najwyraźniej
tr
buforuje. Naucz się czegoś nowego każdego dnia!EDYCJA :
Gdy się nad tym zastanawiam, wyodrębniliśmy przyczynę, ale nie dostarczyliśmy wyjaśnienia. Jeśli
cat | tr
, to pisze od razu, jeślicat | sed
, to pisze od razu, ale jeślitr | sed
, to czeka naEOF
. Sugerowałbym, że odpowiedź może być zakopana w kodzie źródłowymtr
lubsed
nie, a nie stanowić problem z potokiem.EDYCJA :
Widzę, że Wumpus dostarczył wyjaśnienie podczas pisania ostatniej edycji. Dzięki!
źródło
stdbuf
co może być również pomocne. unix.stackexchange.com/questions/182537/...