Асинхронно потребляющая труба с башом

У меня есть такой скрипт

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.

Другие вопросы по тегам