Асинхронно потребляющая труба с башом
У меня есть такой скрипт
data_generator_that_never_guits | while read data
do
an_expensive_process_with data
done
Первый процесс непрерывно генерирует события (с нерегулярными интервалами), которые необходимо обрабатывать по мере их появления. Проблема этого сценария в том, что read on потребляет одну строку вывода; и так как обработка очень дорогая, я бы хотел, чтобы она использовала все данные, которые в настоящее время доступны. С другой стороны, обработка должна начаться немедленно, если новые данные станут доступными. В двух словах, я хочу сделать что-то вроде этого
data_generator_that_never_guits | while read_all_available data
do
an_expensive_process_with data
done
где команда read_all_available будет ждать, если данные не будут доступны для потребления, или скопировать все доступные в настоящее время данные в переменную. Это прекрасно, если данные не состоят из полных строк. По сути, я ищу аналог чтения, который будет читать весь буфер канала, а не читать только одну строку из канала.
Для любопытных среди вас подопечный вопрос, что у меня есть скрипт сборки, который должен вызвать перестроение при изменении исходного файла. Я хочу избегать слишком частых перестроек. Пожалуйста, не предлагайте мне использовать grunt, gulp или другие доступные системы сборки, они не подходят для моих целей.
Спасибо!
2 ответа
Я думаю, что нашел решение после того, как получил лучшее представление о том, как работают субоболочки. Этот скрипт, кажется, делает то, что мне нужно:
data_generator_that_never_guits | while true
do
# wait until next element becomes available
read LINE
# consume any remaining elements — a small timeout ensures that
# rapidly fired events are batched together
while read -t 1 LINE; do true; done
# the data buffer is empty, launch the process
an_expensive_process
done
Можно было бы собрать все строки чтения в одну партию, но на данный момент мне не важно их содержимое, так что я не стал разбираться с этой частью:)
Добавлено 25.09.2014
Вот последняя подпрограмма, на случай, если она может кому-нибудь пригодиться:
flushpipe() {
# wait until the next line becomes available
read -d "" buffer
# consume any remaining elements — a small timeout ensures that
# rapidly fired events are batched together
while read -d "" -t 1 line; do buffer="$buffer\n$line"; done
echo $buffer
}
Быть использованным так:
data_generator_that_never_guits | while true
do
# wait until data becomes available
data=$(flushpipe)
# the data buffer is empty, launch the process
an_expensive_process_with data
done
Что-то вроде read -N 4096 -t 1
может сделать трюк, или, возможно, read -t 0
с дополнительной логикой. См. Руководство по Bash для деталей. В противном случае вам, возможно, придется перейти с Bash на Perl.