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

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

long_running_command | print_progress

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

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

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

337 голосов | спросил Aaron Digulla 16 J0000006Europe/Moscow 2009, 14:27:38

13 ответов


210

Вы можете использовать команду expect unbuffer , например

unbuffer long_running_command | print_progress

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

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

unbuffer x | unbuffer -p y | z
ответил 16 J0000006Europe/Moscow 2009, 15:03:53
396

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

stdbuf -i0 -o0 -e0 command

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

stdbuf -oL -eL command

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

ответил a3nm 19 J0000006Europe/Moscow 2011, 11:12:24
60

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

script -q /dev/null long_running_command | print_progress      # FreeBSD, Mac OS X
script -c "long_running_command" /dev/null | print_progress    # Linux
ответил chad 19 Jpm1000000pmSat, 19 Jan 2013 17:05:05 +040013 2013, 17:05:05
53

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

grep --line-buffered

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

sed -u

Сделать буферную строку вывода.

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

ответил yaneku 31 +04002012-10-31T20:00:13+04:00312012bEurope/MoscowWed, 31 Oct 2012 20:00:13 +0400 2012, 20:00:13
45

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

 socat EXEC:long_running_command,pty,ctty STDIO 

Что он делает, это

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

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

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

ответил shodanex 17 J0000006Europe/Moscow 2009, 11:21:04
17

Вы можете использовать

long_running_command 1>&2 |& print_progress

Проблема в том, что libc будет линейным буфером при выводе stdout на экран и полным буфером при stdout в файл. Но no-buffer для stderr.

Я не думаю, что это проблема с буфером pipe, это все о политике буфера libc.

ответил Wang HongQin 4 AMpFri, 04 Apr 2014 10:10:13 +040010Friday 2014, 10:10:13
10

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

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

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

ответил Jonathan Leffler 17 J0000006Europe/Moscow 2009, 04:47:44
6

Я знаю, что это старый вопрос, и у меня было много ответов, но если вы хотите избежать проблемы с буфером, просто попробуйте что-то вроде:

stdbuf -oL tail -f /var/log/messages | tee -a /home/your_user_here/logs.txt

Это будет выводить в режиме реального времени журналы и также сохранять их в файле logs.txt, и буфер больше не будет влиять на команду tail -f. р>

ответил Marin Nedea 7 AM000000110000004331 2015, 11:31:43
4

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

ответил 16 J0000006Europe/Moscow 2009, 14:45:24
2

В том же ключе, что и ответ чада , вы можете написать небольшой скрипт следующим образом:

# save as ~/bin/scriptee, or so
script -q /dev/null sh -c 'exec cat > /dev/null'

Затем используйте эту команду scriptee в качестве замены tee.

my-long-running-command | scriptee

Увы, я не могу показаться, что такая версия отлично работает в Linux, поэтому, похоже, это унифицированные стили BSD.

В Linux это близко, но вы не возвращаете свое приглашение, когда оно заканчивается (пока вы не нажмете enter и т. д.) ...

script -q -c 'cat > /proc/self/fd/1' /dev/null
ответил jwd 18 52016vEurope/Moscow11bEurope/MoscowFri, 18 Nov 2016 04:12:09 +0300 2016, 04:12:09
1

Согласно здесь, здесь , вы может попытаться уменьшить канал ulimit до одного 512-байтового блока. Он, конечно же, не отключит буферизацию, но хорошо, 512 байт меньше, чем 4K: 3

ответил RAKK 20 Maypm14 2014, 23:43:13
1

Я нашел это умное решение: (echo -e "cmd 1\ncmd 2" && cat) | ./shell_executable

Это трюк. cat будет читать дополнительный ввод (до EOF) и передать его в канал после того, как echo поместил свои аргументы во входной поток shell_executable.

ответил jaggedsoft 4 52016vEurope/Moscow11bEurope/MoscowFri, 04 Nov 2016 14:01:37 +0300 2016, 14:01:37
-1

Согласно this размер буфера для буфера, похоже, установлен в ядре, и вам потребуется перекомпилировать ваше ядро ​​для изменения.

ответил 16 J0000006Europe/Moscow 2009, 14:41:45

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

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132