Промывка стандартного вывода, кажется, не имеет никакого эффекта, если чтение со стандартного ввода - PullRequest
0 голосов
/ 09 ноября 2019

У меня есть следующий сценарий:

#!/usr/bin/perl -w
use strict;
$| = 1;
foreach (1..5) {
    print $_ . "\r";
    sleep 1;
}
print "\n";

Это ведет себя как ожидалось: числа 1,2,3,4,5 перезаписывают друг друга на консоли.

$ ./loop.pl | hexdump -C
00000000  31 0d 32 0d 33 0d 34 0d  35 0d 0a                 |1.2.3.4.5..|

Однако другой сценарий (предназначенный для скрытия большого вывода долго работающей программы, такой как: long_running_program | tee output | ./progr)

#!/usr/bin/perl -w
use strict;
$| = 1;
while (<>) {
    chomp;
    print $_ . "\r";
}
print "\n";

, вызывает другое поведение, когда вводперенаправлено:

 perl -wle 'foreach (1..5) { print $_; sleep 1 }' | ./progr.pl

Нет вывода в течение пяти секунд, затем отображается «5». Тем не менее hexdump показывает тот же результат (через пять секунд)

$ perl -wle 'foreach (1..5) { print $_; sleep 1 }' | ./progr.pl | hexdump.exe -C
00000000  31 0d 32 0d 33 0d 34 0d  35 0d 0a                 |1.2.3.4.5..|

Это не специфично для Perl. Следующий код C

for (int i = 0; i < 6; ++i) {
    printf("%d\r", i);
    fflush(stdout);
    sleep(1);
}
puts("\n");

показывает цифры, перезаписывающие друг друга, но

#define SIZE (256 * 1024)
char buffer[SIZE];
int line = 0;
while (fgets(buffer, SIZE, stdin)) {
    printf("%d\r", ++line);
    fflush(stdout);
}
puts("\n");

в конце канала отображает вывод только после исчерпания ввода.

Даже

setvbuf(stdout, NULL, _IONBF, 0);

, кажется, не помогает.

Я пробовал все это через SSH-соединение с удаленной системой Linux (RHEL6) и локально под Cygwin.

(Отредактировано с исправлениями @Fredrik и @usr)

Ответы [ 2 ]

2 голосов
/ 09 ноября 2019

Вы смотрите не ту программу. Вы отключили буферизацию вывода во второй программе конвейера, но не в первой.


STDOUT буферизуется в строке, если он подключен к терминалу, буферизуется в другом блоке.

Буферизация строки: сбрасывается при выводе перевода строки.

Буферизация блока: сбрасывается при заполнении буфера.

Начиная с STDOUT первой программыТрубопровод (генератор входов) подключен к каналу, его выход является блочно-буферизованным. А поскольку буфер достаточно большой, чтобы вместить весь вывод программы, ваш генератор ввода фактически ничего не выводит, пока не завершится.

Измените

perl -wle 'foreach (1..5) { print $_; sleep 1 }' | ./progr.pl

на

perl -wle '$| = 1; foreach (1..5) { print $_; sleep 1 }' | ./progr.pl

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

unbuffer perl -wle 'foreach (1..5) { print $_; sleep 1 }' | ./progr.pl
0 голосов
/ 09 ноября 2019

В большинстве этих примеров вы передаете выходные данные, которые имеют только возврат каретки (до тех пор, пока программа не завершится и не выведет новую строку (или две для версии C)) в программы, которые одновременно читают строку, оканчивающуюся новой строкой. ,Конечно, вы не увидите выходные данные второй программы до тех пор, пока не завершится первая программа - она ​​ожидает на новой строке или в конце файла, чтобы вернуть первую строку.


В случае perl -wle 'foreach (1..5) { print $_; sleep 1 }' | ./progr.pl, да, есть опции новой строки благодаря опции -l, но вывод буферизуется, потому что это в канал, а progr.pl не видит никакого вводапока первая часть не закончится. Добавьте $|=1; в начале перед циклом, и вы получите другие результаты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...