Примусово буферизувати stdout при трубопроводі до трійника


117

Зазвичай stdoutце буферні лінії. Іншими словами, доки ваш printfаргумент закінчується новим рядком, ви можете очікувати, що рядок буде надруковано миттєво. Схоже, це не затримується при використанні труби для переадресації tee.

У мене є програма C ++ a, яка виводить рядки, завжди \nзавершені до stdout.

Коли він запускається сам ( ./a), все друкується правильно та в потрібний час, як очікувалося. Однак якщо я передаю його tee( ./a | tee output.txt), він нічого не надрукує, поки не вийде, що перешкоджає використанню мети tee.

Я знаю, що я міг це виправити, додаючи fflush(stdout)після кожної операції друку в програму C ++. Але чи є чистіший, простіший спосіб? Чи є команда, яку я можу запустити, наприклад, яка змусить stdoutбути буферизованими рядками, навіть коли використовує трубу?

Відповіді:


67

Спробуйте, unbufferщо є частиною expectпакету. Можливо, це вже є у вашій системі.

У вашому випадку ви б використовували його так:

./a | unbuffer -p tee output.txt

( -pпризначений для конвеєрного режиму, коли unbuffer читає з stdin та передає його команді в решті аргументів)


Дякую, це спрацювало, хоча мені довелося скласти expectсебе, оскільки unbuffer, здається, не включено за замовчуванням в OS X.
houbysoft

@houbysoft: Я радий, що це працювало на тебе. unbufferце лише невеликий сценарій, тому вам не потрібно було б перекомпілювати весь пакет.
Призупинено до подальшого повідомлення.

Так, напевно, ні, але ./configure && makeпройшло близько 10 секунд, а потім я просто перейшов unbufferдо /usr/local/bin:)
houbysoft

3
Я встановив його на моєму mac (10.8.5) через brew: brew install очікуйте --with-brews-tk
Nils

2
FWIW, оскільки unbuffer дещо плутає, відповідна структура є unbuffer {commands with pipes/tee}.
Підроблене ім’я

128

Ви можете спробувати stdbuf

$ stdbuf -o 0 ./a | tee output.txt

(велика) частина довідкової сторінки:

  -i, --input=MODE   adjust standard input stream buffering
  -o, --output=MODE  adjust standard output stream buffering
  -e, --error=MODE   adjust standard error stream buffering

If MODE is 'L' the corresponding stream will be line buffered.
This option is invalid with standard input.

If MODE is '0' the corresponding stream will be unbuffered.

Otherwise MODE is a number which may be followed by one of the following:
KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.
In this case the corresponding stream will be fully buffered with the buffer
size set to MODE bytes.

майте це на увазі, однак:

NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does
for e.g.) then that will override corresponding settings changed by 'stdbuf'.
Also some filters (like 'dd' and 'cat' etc.) dont use streams for I/O,
and are thus unaffected by 'stdbuf' settings.

ви не працюєте stdbufна tee, ви працюєте його a, так що це не повинно вплинути на вас, якщо ви не встановите буферизацию a«потоків s в a" s джерела.

Крім того , stdbufце НЕ POSIX, а частина GNU-Coreutils.


3
Дякую, але це здається недоступним для OS X (питання позначено тегом osx-lion).
houbysoft

2
@houbysoft - Я впевнений, що інструменти GNU можна встановити на OS X
jordanm

1
@jordanm: можливо, але встановлення всіх інструментів GNU виглядає як надмірне для цього ...
houbysoft

1
Цю відповідь схвалили, оскільки stdbufвона вже доступна в дистрибутивах Centos Linux, які ми використовуємо, і unbufferне є. Дякую!
Х'ю Уолтерс

6
Для сценарію python stdbuf не працюватиме, але ви можете використовувати -uдля відключення буферизації на стороні python:python3 -u a.py | tee output.txt
Honza

27

Ви також можете спробувати виконати свою команду в псевдотерміналі, використовуючи scriptкоманду (яка повинна примусити виводити лінійку в буфер)!

script -q /dev/null ./a | tee output.txt     # Mac OS X, FreeBSD
script -c "./a" /dev/null | tee output.txt   # Linux

Будьте в курсі, що scriptкоманда не повертає назад статус виходу загорнутої команди.


3
script -t 1 /path/to/outputfile.txt ./aдобре працював для мого використання. У прямому ефірі він передає весь вихід, outputfile.txtа також друкує його у вигляд оболонки. Не потрібно було користуватисяtee
Пітер Берг

26

Ви можете використовувати setlinebuf від stdio.h.

setlinebuf(stdout);

Це має змінити буферизацію на "буферну лінію".

Якщо вам потрібна більша гнучкість, ви можете використовувати setvbuf.


8
Цікаво, чому це рішення має так мало відгуків. Це єдине рішення, яке не накладає тягар на абонента.
оксиген

1
Зауважте, що це не стандартний C (або навіть POSIX). Мабуть, краще використовувати setvbuf(stdout, NULL, _IOLBF, 0), що точно рівнозначно.
rvighne

Це вирішило мою проблему на OS X Catalina з програмою C ++, яка була надрукована (і), і я перекладав трійник, але бачив вихід лише тоді, коли програма закінчилася.
jbaxter

2

Якщо ви замість цього використовуєте потокові класи C ++, кожен std::endl - це неявна флеш. Використовуючи друк у стилі С, я думаю, що метод, який ви запропонували ( fflush()), є єдиним способом.


4
На жаль, це неправда. Ви можете спостерігати однакову поведінку з c ++ std :: cout, навіть коли використовуєте std :: endl або std :: flush. Буферизація відбувається вгорі, і найпростішим рішенням в Linux здається setlinebuf (stdout); як перший рядок у main (), коли ви автор програми та використовуєте інші вище рішення, коли не в змозі змінити вихідний код.
оксиген

1
@oxygene Це неправда. Я спробував це, і ендл змиває буфер під час передачі до трійника (на відміну від printf). Код: #include <iostream> #include <unistd.h> int main(void) { std::cout << "1" << std::endl; sleep(1); std::cout << "2" << std::endl; }. endl завжди промиває
Кертіс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.