Почему вывод mawk (STDOUT) буферизован, даже если это терминал?

Вопрос или проблема

Я осведомлён о том, что STDOUT обычно буферизуется такими командами, как mawk (но не gawk), grep, sed и так далее, если не использовать соответствующие опции (т.е. mawk --Winteractive, или grep --line-buffered, или sed --unbuffered). Но буферизация не происходит, когда STDOUT является терминалом/TTY, в этом случае она буферизуется по строкам.

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

Простой пример :

$ while sleep 3; do echo -n "Текущее время: ";date +%T; done | mawk '{print $NF}'
^C

Ничего не происходит долгое время, потому что mawk похоже накапливает свой вывод в буфере.

Я этого не ожидал. mawk‘s вывод — это терминал, так зачем же его STDOUT буферизуется?

Действительно, с опцией -Winteractive вывод отображается каждые 3 секунды :

$ while sleep 3; do echo -n "Текущее время: ";date +%T; done | mawk -Winteractive '{print $NF}'
10:57:05
10:57:08
10:57:11
^C

Теперь это поведение явно связано с mawk, потому что оно не воспроизводится, если я использую, например, grep. Даже без опции --line-buffered grep не буферизует свой STDOUT, что является ожидаемым поведением, учитывая, что grep‘s STDOUT — это терминал :

$ while sleep 3; do echo -n "Текущее время: ";date +%T; done | grep Current
Текущее время: 11:01:44
Текущее время: 11:01:47
Текущее время: 11:01:50
^C

Дело не в том, что он буферизует свой вывод.

mawk — это единственная утилита, которую я знаю, которая буферизует свой ввод.

Смотрите также https://github.com/ThomasDickey/original-mawk/issues/41#issuecomment-241070898

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

Вы можете проверить это, выполнив:

(echo 1; sleep 1; echo 2) | mawk '{system("echo "$1)}'

Это можно отключить с помощью опции -Winteractive. Обратите внимание, что с -Winteractive записи представляют собой строки, независимо от значения RS.

просто делюсь своим опытом в Ubuntu

gawk

буферизация по строкам,
Не требуется никаких ухищрений

{ echo 1; sleep 1; echo 2; } | mawk '{print}'

mawk

работает только с -Winteractive

{ echo 1; sleep 2; echo 2; } | mawk -Winteractive '{print}'

эти трюки не работают:

  • stdbuf -oL
  • fflush()
  • system(“”)
{ echo 1; sleep 1; echo 2; } | mawk '{print}'
{ echo 1; sleep 2; echo 2; } | mawk '{print; fflush(); system("");}'
{ echo 1; sleep 2; echo 2; } | stdbuf -oL mawk '{print}'
{ echo 1; sleep 2; echo 2; } | stdbuf -oL mawk '{print; fflush(); system("");}'

Ответ или решение

Почему стандартный вывод mawk имеет буферизацию, даже когда он направляется в терминал?

Когда мы работаем с командами в Unix-подобных системах, важно понимать, как обрабатывается ввод и вывод. В случае утилиты mawk, стандартный вывод (STDOUT) может вести себя иначе, чем ожидается, особенно в ситуациях, когда вывод направляется в терминал. Давайте разберем это подробно.

1. Понимание буферизации в Unix

Буферизация — это механизм, используемый для повышения эффективности ввода-вывода. Обычно стандартный вывод программы буферизуется для повышения производительности. В случае mawk, вы можете столкнуться с проблемой буферизации его стандартного вывода, когда вывод идет через конвейер (pipe). Эта буферизация происходит на уровне ввода в mawk, что является основной причиной задержек в выводе.

2. Почему mawk буферизует ввод?

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

3. Пример: Обработка данных с использованием pipeline

Рассмотрим пример:

while sleep 3; do echo -n "Current Time is "; date +%T; done | mawk '{print $NF}'

Здесь mawk будет ждать, пока не накопит определенное количество записей во входном буфере, прежде чем начать вывод времени. Вы не увидите никакого вывода в терминале, пока не пройдет достаточно времени, чтобы mawk собрал данные.

4. Сравнение с другими утилитами

В отличие от mawk, такие утилиты, как grep, не ведут себя подобным образом. Используя grep, вы можете получить мгновенный вывод, даже без дополнительных опций, так как grep сразу обрабатывает и выводит данные, когда они поступают на стандартный ввод.

5. Как изменить поведение mawk?

Чтобы отключить буферизацию во mawk, вы можете использовать опцию -Winteractive. Это заставит mawk работать в интерактивном режиме, что позволяет ему немедленно выводить каждую строку при её поступлении:

while sleep 3; do echo -n "Current Time is "; date +%T; done | mawk -Winteractive '{print $NF}'

Таким образом, с данной опцией вывод будет происходить каждые три секунды, как и ожидалось.

Заключение

Буферизация ввода в mawk является уникальной особенностью, которая может привести к неожиданному поведению, если не знать о ней. Понимание того, как именно работает каждая утилита, позволяет избежать путаницы и сделать ваше взаимодействие с инструментами Unix более предсказуемым. Использование опции -Winteractive предоставляет контроль над выводом и обеспечивает то, чтобы результаты немедленно отображались на терминале.

Понять, как работают операционные системы и их инструменты, — это ключ к успешному управлению и оптимизации рабочих процессов в области IT.

Оцените материал
Добавить комментарий

Капча загружается...