Mam skrypt, który wywołuje dwa polecenia:
long_running_command | print_progress
Te long_running_command
odciski postęp, ale jestem zadowolony z niego. Używam, print_progress
aby uczynić to ładniejszym (mianowicie, drukuję postęp w jednym wierszu).
Problem: Podłączenie rury do standardowego wyjścia również aktywuje bufor 4K, do ładnego programu do drukowania nic nie dostaje ... nic ... nic ... dużo ... :)
Jak mogę wyłączyć bufor 4K dla long_running_command
(nie, nie mam źródła)?
Odpowiedzi:
Możesz użyć
unbuffer
polecenia (które jest częściąexpect
pakietu), npunbuffer
łączy sięlong_running_command
z pseudoterminalem (pty), co sprawia, że system traktuje go jako proces interaktywny, dlatego nie wykorzystuje buforowania 4-kiB w potoku, który jest prawdopodobną przyczyną opóźnienia.W przypadku dłuższych potoków może być konieczne cofnięcie buforowania każdego polecenia (z wyjątkiem ostatniego), np
źródło
expect_unbuffer
i znajduje się wexpect-dev
pakiecie, a nie wexpect
pakiecieexpect-dev
zapewnia obaunbuffer
iexpect_unbuffer
(ten pierwszy jest dowiązaniem symbolicznym do drugiego). Linki są dostępne odexpect 5.44.1.14-1
(2009).Innym sposobem na skórowanie tego kota jest użycie
stdbuf
programu, który jest częścią GNU Coreutils (FreeBSD ma również swój własny).To całkowicie wyłącza buforowanie danych wejściowych, wyjściowych i błędów. W przypadku niektórych aplikacji buforowanie linii może być bardziej odpowiednie ze względu na wydajność:
Pamiętaj, że działa tylko w przypadku
stdio
buforowania (printf()
,fputs()
...) w przypadku dynamicznie połączonych aplikacji i tylko wtedy, gdy aplikacja sama nie dostosuje buforowania swoich standardowych strumieni, chociaż powinno to obejmować większość aplikacji.źródło
sudo stdbuff … command
prace, chociażstdbuff … sudo command
nie.stdbuf
nie działatee
, ponieważtee
zastępuje wartości domyślne ustawione przezstdbuf
. Zobacz stronę podręcznika użytkownikastdbuf
.stdbuf
używaLD_PRELOAD
mechanizmu do wstawiania własnej dynamicznie ładowanej bibliotekilibstdbuf.so
. Oznacza to, że nie będzie działać z tego rodzaju plikami wykonywalnymi: z ustawionymi możliwościami setuid lub file, statycznie połączonymi, nieużywającymi standardowego libc. W takich przypadkach lepiej jest używać rozwiązań zunbuffer
/script
/socat
. Zobacz także stdbuf z setuid / Możliwościami .Jeszcze innym sposobem włączenia trybu wyjścia buforowania linii dla
long_running_command
jest użyciescript
polecenia, które uruchamia twójlong_running_command
pseudo terminal (pty).źródło
script
jest to tak stare polecenie, powinno być dostępne na wszystkich platformach uniksowych.-q
w systemie Linux:script -q -c 'long_running_command' /dev/null | print_progress
stdin
, co uniemożliwia uruchomienie takiegolong_running_command
w tle, przynajmniej po uruchomieniu z terminalu interaktywnego. Aby obejść ten problem , mogłem przekierować standardowe wejście/dev/null
, ponieważ mojelong_running_command
nie używastdin
.Na
grep
,sed
iawk
można wymusić wyjście być linia buforowane. Możesz użyć:Wymusza buforowanie wyjścia. Domyślnie wyjście jest buforowane liniowo, gdy standardowe wyjście jest terminalem, a blok buforowany w inny sposób.
Spraw, aby linia wyjściowa była buforowana.
Zobacz tę stronę, aby uzyskać więcej informacji: http://www.perkin.org.uk/posts/how-to-fix-stdio-buffering.html
źródło
Jeśli jest problem z libc modyfikującym buforowanie / opróżnianie, gdy dane wyjściowe nie trafiają do terminala, powinieneś spróbować socat . Możesz utworzyć dwukierunkowy strumień między prawie każdym rodzajem mechanizmu we / wy. Jednym z nich jest rozwidlony program mówiący do pseudo tty.
Co to robi
Jeśli daje to taką samą wydajność jak
long_running_command
, możesz kontynuować z potokiem.Edycja: Wow Nie widziałem odpowiedzi niebuforowanej! Cóż, socat i tak jest świetnym narzędziem, więc mogę zostawić tę odpowiedź
źródło
socat -u exec:long_running_command,pty,end-close -
tutajMożesz użyć
Problem polega na tym, że libc będzie buforował linię, gdy standardowe wyjście do ekranu, i pełny bufor, gdy standardowe wyjście do pliku. Ale brak bufora dla stderr.
Nie sądzę, że to jest problem z buforem potoku, wszystko dotyczy polityki buforów libc.
źródło
zsh
(skąd|&
pochodzi adaptacja z csh), abash
kiedy to zrobiszcmd1 >&2 |& cmd2
, oba fd 1 i 2 są podłączone do zewnętrznego wyjścia. Działa więc w zapobieganiu buforowaniu, gdy ten zewnętrzny stdout jest terminalem, ale tylko dlatego, że wyjście nie przechodzi przez potok (więcprint_progress
nic nie drukuje). Więc to jest tak samo jaklong_running_command & print_progress
(z wyjątkiem tego, że print_progress stdin jest potokiem, który nie ma programu piszącego). Możesz zweryfikować wls -l /proc/self/fd >&2 |& cat
porównaniu dols -l /proc/self/fd |& cat
.|&
jest skrótem2>&1 |
. Takcmd1 |& cmd2
jestcmd1 1>&2 2>&1 | cmd2
. Tak więc, zarówno fd 1, jak i 2 kończą się na oryginalnym stderr, i nic nie zostaje zapisywane na rurze. (s/outer stdout/outer stderr/g
w moim poprzednim komentarzu).Kiedyś tak było i prawdopodobnie nadal tak jest, że kiedy standardowe wyjście jest zapisywane na terminalu, to jest ono domyślnie buforowane w linii - kiedy zapisywany jest nowy wiersz, wiersz jest zapisywany na terminalu. Gdy standardowe wyjście jest wysyłane do potoku, jest w pełni buforowane - więc dane są wysyłane do następnego procesu w potoku, gdy standardowy bufor We / Wy jest wypełniony.
To jest źródłem kłopotów. Nie jestem pewien, czy można wiele zrobić, aby to naprawić bez modyfikacji programu zapisującego w potoku. Możesz użyć
setvbuf()
funkcji z_IOLBF
flagą, aby bezwarunkowo przejśćstdout
do trybu buforowania linii. Ale nie widzę łatwego sposobu na wymuszenie tego w programie. Lub program może zrobićfflush()
w odpowiednich punktach (po każdym wierszu wyniku), ale stosuje się ten sam komentarz.Przypuszczam, że jeśli zastąpisz potok pseudo-terminalem, wówczas standardowa biblioteka I / O uznałaby, że wyjście jest terminalem (ponieważ jest to rodzaj terminala) i automatycznie buforowałaby linię. Jest to jednak złożony sposób radzenia sobie z rzeczami.
źródło
Wiem, że to stare pytanie i już miałem wiele odpowiedzi, ale jeśli chcesz uniknąć problemu z buforem, po prostu spróbuj czegoś takiego:
Spowoduje to wyświetlenie dzienników w czasie rzeczywistym, a także zapisanie ich w
logs.txt
pliku, a bufor nie będzie już wpływał natail -f
polecenie.źródło
NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does for example) then that will override corresponding changes by 'stdbuf'.
Nie sądzę, że problem tkwi w fajce. Wygląda na to, że twój długotrwały proces nie czyści wystarczająco własnego bufora. Zmiana rozmiaru bufora potoku byłaby włamaniem do obejścia go, ale nie sądzę, że jest to możliwe bez przebudowania jądra - coś, czego nie chciałbyś robić jako włamania, ponieważ prawdopodobnie wywrze negatywny wpływ na wiele innych procesów.
źródło
Zgodnie z tym postem tutaj możesz spróbować zmniejszyć ulimit rury do jednego pojedynczego 512-bajtowego bloku. Z pewnością nie wyłączy buforowania, ale cóż, 512 bajtów to znacznie mniej niż 4K: 3
źródło
Podobnie jak odpowiedź Czada , możesz napisać taki skrypt:
Następnie użyj tego
scriptee
polecenia jako zamiennika dlatee
.Niestety, nie wydaje mi się, aby taka wersja działała idealnie w systemie Linux, więc wydaje się ograniczona do uniksów w stylu BSD.
W systemie Linux jest to blisko, ale nie wyświetla się monit po jego zakończeniu (dopóki nie naciśniesz enter itp.) ...
źródło
script
emuluje terminal, więc tak, uważam, że wyłącza buforowanie. Powtarza także echo każdej wysłanej do niej postaci - i dlategocat
jest wysyłana/dev/null
w tym przykładzie. Jeśli chodzi o uruchomiony programscript
, rozmawia on z sesją interaktywną. Myślę, żeexpect
pod tym względem jest podobny , alescript
prawdopodobnie jest częścią twojego podstawowego systemu.tee
jest wysłanie kopii strumienia do pliku. Gdzie jest określony plikscriptee
?cat
polecenietee myfile.txt
i powinieneś uzyskać pożądany efekt.Znalazłem to sprytne rozwiązanie:
(echo -e "cmd 1\ncmd 2" && cat) | ./shell_executable
To załatwia sprawę.
cat
odczyta dodatkowe dane wejściowe (do EOF) i przekaże je do potoku poecho
umieszczeniu swoich argumentów w strumieniu wejściowymshell_executable
.źródło
cat
nie widzi danych wyjściowychecho
; po prostu uruchom dwa polecenia w podpowłoce, a dane wyjściowe obu zostaną przesłane do potoku. Drugie polecenie w podpowłoce („cat”) czyta ze standardowego / zewnętrznego standardowego wejścia, dlatego działa.Zgodnie z tym rozmiar bufora potoku wydaje się być ustawiony w jądrze i wymaga zmiany kompilacji jądra.
źródło