Асинхронное чтение стандартного вывода

Я написал этот простой скрипт, он генерирует одну строку вывода в секунду (generator.sh):

for i in {0..5}; do echo $i; sleep 1; done

Программа raku запустит этот скрипт и напечатает строки, как только они появятся:

my $proc = Proc::Async.new("sh", "generator.sh");
$proc.stdout.tap({ .print });
my $promise = $proc.start;
await $promise;

Все работает как положено: каждую секунду мы видим новую строку. Но перепишем генератор в раку (generator.raku):

for 0..5 { .say; sleep 1 }

И измените первую строку программы на это:

my $proc = Proc::Async.new("raku", "generator.raku");

Теперь что-то не так: сначала мы видим первую строку вывода ("0"), потом долгая пауза и, наконец, видим все оставшиеся строки вывода.

Я попытался получить вывод генераторов с помощью команды script:

script -c 'sh generator.sh' script-sh
script -c 'raku generator.raku' script-raku

А проанализировать их в шестнадцатеричном редакторе, и похоже, что они одинаковые: после каждой цифры следуют байты 0d и 0a.

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

Типы данных JavaScript
Типы данных JavaScript
В JavaScript существует несколько типов данных, включая примитивные типы данных и ссылочные типы данных. Вот краткое объяснение различных типов данных...
Как сделать движок для футбольного матча? (простой вариант)
Как сделать движок для футбольного матча? (простой вариант)
Футбол. Для многих людей, живущих на земле, эта игра - больше, чем просто спорт. И эти люди всегда мечтают стать футболистом или менеджером. Но, к...
Знайте свои исключения!
Знайте свои исключения!
В Java исключение - это событие, возникающее во время выполнения программы, которое нарушает нормальный ход выполнения инструкций программы. Когда...
CSS Flex: что должен знать каждый разработчик
CSS Flex: что должен знать каждый разработчик
CSS Flex: что должен знать каждый разработчик Модуль flexbox, также известный как гибкий модуль разметки box, помогает эффективно проектировать и...
Введение в раздел "Заголовок" в HTML
Введение в раздел "Заголовок" в HTML
Говорят, что лучшее о человеке можно увидеть только изнутри, и это относится и к веб-страницам HTML! Причина, по которой некоторые веб-страницы не...
12
0
177
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий
Почему такая разница в работе с вроде бы одинаковыми генераторами?

Во-первых, что касается названия, проблема не в том, кто его читает, а в том, что он пишет.

Реализация ввода-вывода Raku проверяет, подключен ли STDOUT к TTY. Если это TTY, любой вывод немедленно записывается в дескриптор вывода. Однако, если это не TTY, то будет применена буферизация, что приведет к значительному повышению производительности, но за счет разбиения вывода на части по размеру буфера.

Если вы измените generator.raku, чтобы отключить буферизацию вывода:

$*OUT.out-buffer = False; for 0..5 { .say; sleep 1 }

Тогда результат будет виден сразу.

Мне нужно понять это, потому что я собираюсь запустить внешнюю программу и обрабатывать ее вывод онлайн.

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

В дополнение к ответу @Jonathan Worthington. Хотя буферизация является проблемой на стороне записи, с этим можно справиться на стороне чтения. stdbuf, unbuffer, script можно использовать в Linux (см. это обсуждение). На windows мне помогает только winpty, который я нашел здесь.

Итак, если в рабочем каталоге есть файлы winpty.exe, winpty-agent.exe, winpty.dll, msys-2.0.dll, этот код можно использовать для запуска программы без буферизации:

my $proc = Proc::Async.new(<winpty.exe -Xallow-non-tty -Xplain raku generator.raku>);

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