Выключите буферизацию в канале

У меня есть сценарий, который называет две команды:

long_running_command | print_progress

long_running_command печать прогрессирует, но я недоволен им. Я использую print_progress сделать это более хорошим (а именно, я печатаю прогресс одной строки).

Проблема: Соединение канал к stdout также активирует буфер 4K, таким образом, хорошая программа печати ничего не получает... ничто... ничто... много... :)

Как я могу отключить 4K буфер для long_running_command (не, у меня нет источника)?

408
27.01.2020, 12:22
8 ответов

Можно использовать unbuffer команда (который стал частью expect пакет), например.

unbuffer long_running_command | print_progress

unbuffer подключения к long_running_command через псевдотерминал (имущество), которое заставляет систему рассматривать его как интерактивный процесс, поэтому не используя буферизацию 4 кибибитов в конвейере, который является вероятной причиной задержки.

Для более длинных конвейеров Вам, вероятно, придется освободить буфер каждая команда (кроме заключительной), например.

unbuffer x | unbuffer -p y | z
261
27.01.2020, 19:28
  • 1
    На самом деле использование имущества для соединения с интерактивными процессами верно для, ожидают в целом. –   17.06.2009, 10:58
  • 2
    Чтобы конвейерная обработка вызовов освободила буфер, необходимо использовать-p аргумент так, чтобы освободили буфер чтения от stdin. –   06.10.2009, 23:18
  • 3
    Отметьте: В debian системах это называют expect_unbuffer и находится в expect-dev пакет, не expect пакет –  bdonlan 24.01.2011, 13:14
  • 4
    @bdonlan: По крайней мере, на (находящейся в debian) Ubuntu, expect-dev предоставляет обоим unbuffer и expect_unbuffer (первый - символьная ссылка на последнего). Ссылки доступны с тех пор expect 5.44.1.14-1 (2009). –  jfs 11.04.2013, 16:00
  • 5
    unbuffer в основном expect пакет на debian теперь (это - все еще символьная ссылка на expect_unbuffer, который является также в основном expect пакет) –  cas 05.11.2015, 01:50

Другой способ освежевать эту кошку состоит в том, чтобы использовать stdbuf программа, которая является частью Coreutils GNU (FreeBSD также имеет свой собственный).

stdbuf -i0 -o0 -e0 command

Это выключает буферизацию полностью для входа, вывода и ошибки. Для некоторых приложений буферизация строки может более подойти по причинам производительности:

stdbuf -oL -eL command

Обратите внимание, что это только работает на stdio буферизация (printf(), fputs()...) для динамично связанных приложений, и только если то приложение иначе не корректирует буферизацию своих стандартных потоков отдельно, хотя это должно покрыть большинство приложений.

474
27.01.2020, 19:28
  • 1
    , "освобождают буфер" потребности, которые будут установлены в Ubuntu, которая является в пакете: ожидайте-dev, который составляет 2 МБ... –  lepe 27.06.2013, 09:21
  • 2
    Это работает отлично на установке Raspbian по умолчанию для освобождения буфера, регистрируясь. Я нашел sudo stdbuff … command работы, хотя stdbuff … sudo command не сделал. спасибо –  natevw 10.07.2013, 09:05
  • 3
    @qdii stdbuf не работает с tee, потому что tee перезаписывает значения по умолчанию, установленные stdbuf. См. страницу руководства stdbuf. –  ceving 30.06.2014, 14:51
  • 4
    @lepe Причудливо, освобождают буфер, имеет зависимости от x11 и tcl/tk, означая, что ему на самом деле нужно> 80 МБ при установке его на сервере без них. –  lambshaanxy 28.08.2014, 15:27
  • 5
    @qdii stdbuf использование LD_PRELOAD механизм для вставки его собственной динамично загруженной библиотеки libstdbuf.so. Это означает, что не будет работать с этими исполняемыми файлами видов: с setuid или набором возможностей файла, статически связанным, не использующий стандарт libc. В этих случаях лучше использовать решения с unbuffer / script / socat. См. также stdbuf с setuid/capabilities. –  pabouk 12.10.2015, 12:20

Если это - проблема с libc изменение его буферизации / сбрасывание, когда произведенный не переходит к терминалу, необходимо попробовать socat. Можно создать двунаправленный поток почти между любым видом механизма ввода-вывода. Один из тех является разветвленной программой, говорящей с псевдо tty.

 socat EXEC:long_running_command,pty,ctty STDIO 

То, что это делает,

  • создайте псевдо tty
  • ветвление long_running_command с ведомой стороной имущества как stdin/stdout
  • установите двунаправленный поток между основной стороной имущества и вторым адресом (здесь, это - STDIO),

Если это дает Вам тот же вывод как long_running_command, затем можно продолжить канал.

Редактирование: Ничего себе, не видел освобождать буфер ответ! Ну, socat является большим инструментом так или иначе, таким образом, я мог бы просто оставить этот ответ

52
27.01.2020, 19:28
  • 1
    ... и я не знал о socat - взгляды отчасти как netcat только, возможно, больше.;) Спасибо и +1. –   20.06.2009, 12:32
  • 2
    , который я использовал бы socat -u exec:long_running_command,pty,end-close - здесь –  Stéphane Chazelas 07.08.2015, 16:10

Это раньше имело место и вероятно все еще имеет место, что, когда стандартный вывод записан в терминал, это - строка, буферизованная по умолчанию - когда новая строка записана, строка записана в терминал. Когда стандартный вывод отправляется в канал, он полностью буферизуется - таким образом, данные только отправляются в следующий процесс в конвейере, когда стандартный буфер ввода-вывода заполнен.

Это - источник проблемы. Я не уверен, существует ли очень, можно сделать для фиксации его, не изменяя программу, пишущую в канал. Вы могли использовать setvbuf() функция с _IOLBF отметьте для безусловного помещения stdout в строку буферизовал режим. Но я не вижу простой способ осуществить это на программе. Или программа может сделать fflush() в соответствующих точках (после каждой строки вывода), но тот же комментарий применяется.

Я предполагаю, что при замене канала псевдотерминалом, затем стандартная библиотека I/O думала бы, что вывод был терминалом (потому что это - тип терминала), и выровнял бы буфер автоматически. Это - сложный способ иметь дело с вещами, все же.

11
27.01.2020, 19:28

Я не думаю, что проблема с каналом. Это кажется, что Ваш длительный процесс не сбрасывает свой собственный буфер достаточно часто. Изменение размера буфера канала было бы взломом для обхода его, но я не думаю его возможное, не восстанавливая ядро - что-то, что Вы не хотели бы делать как взлом, поскольку это, вероятно, неохотно влияет на большое количество других процессов.

5
27.01.2020, 19:28
  • 1
    Первопричина состоит в том, что libc переключается на буферизацию 4k, если stdout не является tty. –  Aaron Digulla 16.06.2009, 14:50
  • 2
    Это очень интересно! потому что канал не вызывает буферизации. Они обеспечивают буферизацию, но если Вы читаете из канала, Вы добираетесь, любые данные доступны, Вы не должны ожидать буфера в канале. Таким образом, преступник был бы stdio, буферизующим в приложении. –   16.06.2009, 16:58

Согласно этому размер буфера канала, кажется, установлен в ядре и потребовал бы, чтобы Вы перекомпилировали свое ядро для изменения.

-1
27.01.2020, 19:28
  • 1
    , я верю этому, является различным буфером. –  Samuel Edwin Ward 08.01.2013, 23:58

Для grep, sed и awk можно вынудить вывод быть буферизованной строкой. Можно использовать:

grep --line-buffered

Вывод силы, чтобы быть буферизованной строкой. По умолчанию вывод является строкой, буферизованной, когда стандартный вывод является терминалом и блоком, буферизованным иначе.

sed -u

Сделайте выходную строку буферизованной.

Посмотрите эту страницу для получения дополнительной информации: http://www.perkin.org.uk/posts/how-to-fix-stdio-buffering.html

68
27.01.2020, 19:28

Еще один способ включить буферизующий строку режим вывода для long_running_command должен использовать script управляйте что выполнения Ваш long_running_command в псевдотерминале (имущество).

script -q /dev/null long_running_command | print_progress      # FreeBSD, Mac OS X
script -c "long_running_command" /dev/null | print_progress    # Linux
78
27.01.2020, 19:28
  • 1
    +1 хороший прием, с тех пор script такая старая команда, это должно быть доступно на всех подобных Unix платформах. –  Aaron Digulla 20.01.2013, 15:01
  • 2
    Вам также нужно -q на Linux: script -q -c 'long_running_command' /dev/null | print_progress –  jfs 11.04.2013, 15:51
  • 3
    Это походит на чтения сценария от stdin, который лишает возможности выполнять такой long_running_command в фоновом режиме, по крайней мере, при запуске с интерактивного терминала. К обходному решению я смог перенаправить stdin от /dev/null, начиная с моего long_running_command не использует stdin. –  haridsv 15.11.2013, 14:44
  • 4
    Даже работы над Android. –  not2qubit 03.07.2014, 02:36
  • 5
    Один значительный недостаток: ctrl-z больше не работает (т.е. Я не могу приостановить сценарий). Это может быть зафиксировано, например: отзовитесь эхом | sudo сценарий-c/usr/local/bin/ec2-snapshot-all/dev/null | ts, если Вы не возражаете не быть способными взаимодействовать с программой. ответ –  rlpowell 24.07.2015, 03:03

Теги

Похожие вопросы