如果从 stdin 读取,刷新 stdout 似乎没有效果
Flushing stdout appears to have no effect if reading from stdin
我有以下脚本:
#!/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 的更正进行了编辑)
在大多数这些示例中,您将只有回车符 returns 的输出(直到程序结束并打印一个换行符(对于 C 版本为两个换行符))传送到正在读取一个程序的程序一次换行终止行。当然,在第一个程序结束之前,您不会看到第二个程序的输出——它正在等待换行符或文件结尾到 return 第一行。
在 perl -wle 'foreach (1..5) { print $_; sleep 1 }' | ./progr.pl
的情况下,是的,由于 -l
选项,有换行符,但输出是缓冲的,因为它是管道,并且 progr.pl
看不到任何输入,直到第一部分结束。在循环之前的开头添加一个$|=1;
,你会得到不同的结果。
您看错程序了。您在管道的第二个程序中关闭了输出缓冲,但不是第一个。
如果连接到终端,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
我有以下脚本:
#!/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 的更正进行了编辑)
在大多数这些示例中,您将只有回车符 returns 的输出(直到程序结束并打印一个换行符(对于 C 版本为两个换行符))传送到正在读取一个程序的程序一次换行终止行。当然,在第一个程序结束之前,您不会看到第二个程序的输出——它正在等待换行符或文件结尾到 return 第一行。
在 perl -wle 'foreach (1..5) { print $_; sleep 1 }' | ./progr.pl
的情况下,是的,由于 -l
选项,有换行符,但输出是缓冲的,因为它是管道,并且 progr.pl
看不到任何输入,直到第一部分结束。在循环之前的开头添加一个$|=1;
,你会得到不同的结果。
您看错程序了。您在管道的第二个程序中关闭了输出缓冲,但不是第一个。
如果连接到终端,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