Учитывая этот минимальный пример
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; )
он выводит, LINE 1
а затем, через одну секунду, выводит LINE 2
, как и ожидалось .
Если мы передадим это grep LINE
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep LINE
поведение такое же, как и в предыдущем случае, как и ожидалось .
Если, в качестве альтернативы, мы передаем это cat
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | cat
поведение снова такое же, как и ожидалось .
Однако , если мы передадим grep LINE
, а затем cat
,
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep LINE | cat
нет выхода, пока не пройдет одна секунда, и обе строки сразу появятся на выходе, чего я не ожидал .
Почему это происходит и как я могу заставить последнюю версию вести себя так же, как первые три команды?
cat
объединяет файлы. Что вы пытаетесь сделать, проникая вcat
?cat
просто читаетstdin
и выводит вstdout
. Конечно, я придумал этот вопрос с большим количеством сложных вещей вместоecho
иcat
, но они оказались неактуальными, так как проблема обнаруживается с гораздо более простыми примерами.Ответы:
Когда (по крайней мере, GNU)
grep
вывод не является терминалом, он буферизует свой вывод, что и вызывает поведение, которое вы видите. Вы можете отключить этот либо с помощью GNUgrep
«сек--line-buffered
вариант:или
stdbuf
утилита:Отключить буферизацию в трубе есть еще по этой теме.
источник
Упрощенное объяснение
Подобно многим утилитам, это не является чем-то особенным для одной программы, и
grep
ее стандартный вывод варьируется от линейной буферизации до полной буферизации . В первом случае библиотека C буферизует выходные данные в памяти до тех пор, пока либо буфер, содержащий эти данные, не будет заполнен, либо к нему не будет добавлен символ перевода строки (или программа завершится корректно), после чего она вызываетwrite()
фактическую запись содержимого буфера. В последнем случае только заполнение буфера в памяти (или корректное завершение программы) вызываетwrite()
.Более подробное объяснение
Это хорошо известное, но слегка ошибочное объяснение. Фактически, стандартный вывод не буферизован строкой, а умным буферизован в библиотеке GNU C и библиотеке BSD C. Стандартный вывод также сбрасывается, когда при чтении стандартного ввода исчерпывается его буфер в памяти (для ввода с предварительным чтением), и библиотека C должна вызывать,
read()
чтобы получить еще какой-то ввод, и она читает начало новой строки. (Одной из причин этого является предотвращение взаимоблокировки, когда другая программа подключается к обоим концам фильтра и ожидает, что сможет работать построчно, чередуя запись в фильтр и чтение из него; например, «сопроцессы» в GNUawk
например.)Влияние библиотеки C
grep
и другие утилиты делают это - или, точнее, библиотеки C, которые они используют, делают это, потому что это определенная особенность программирования на языке C - на основе того, что они обнаруживают в своем стандартном выводе. Если (и только если) это не интерактивное устройство, они выбирают полную буферизацию, в противном случае они выбирают интеллектуальную буферизацию. Канал считается не интерактивным устройством, потому что определение интерактивного устройства, по крайней мере в мире Unix и Linux, по сути являетсяisatty()
вызовом, возвращающим true для соответствующего файлового дескриптора.Обходные пути для отключения полной буферизации
Некоторые утилиты, такие как,
grep
имеют специфические опции, например,--line-buffered
которые изменяют это решение, которое, как вы можете видеть, имеет неправильное название. Но на самом деле очень малая часть программ-фильтров, которые можно было бы использовать, на самом деле имеет такую возможность.В более общем смысле, можно использовать инструменты, которые копаются во внутренних структурах библиотеки C и изменяют ее принятие решений (которые имеют проблемы с безопасностью, если программа, для которой нужно изменить, имеет установленный UID, а также специфичны для конкретных библиотек C и действительно являются специфичные для программ, написанных на языке C или наложенных поверх него), или такие инструменты,
ptybandage
которые не изменяют внутреннюю часть программы, а просто вставляют псевдотерминал в качестве стандартного вывода, так что решение выходит «интерактивным», чтобы повлиять на это.дальнейшее чтение
источник
grep
, а вызовы базовой библиотеки,setbuf
/setvbuf
. Я не знаю надежного онлайн-справочника по стандарту C, но, например, справочные страницы по Linux и FreeBSD вместе с описанием POSIXsetvbuf
называют его «буферизованной строкой». Даже символическая константа для него есть_IOLBF
.использование
чтобы grep не буферизовал более одной строки за раз.
источник