首个Bash管道中的程序正在阻塞。

4
我将尝试将几个perl程序连接在一起,从我所读的内容来看,管道程序应该同时打开和运行。但是,无论我做什么都似乎并非如此。这里是简化版本:

./video

#!/usr/bin/env perl
my $i = 0;
while($i<10){
    my $time = localtime(time);
    print "VID: $time $i\n";
    sleep 1;
    $i++;
}

./controller

#!/usr/bin/env perl
while(my $line = <STDIN>){
    my $time = localtime(time);
    print "CTRL: $time $line";
}

./pipes

#!/usr/bin/env perl
open(my $controller, "|./controller") || die "Can't fork: $!";
open(STDOUT, ">&", $controller);
open(my $video, "|./video") || die "Can't fork: $!";
print STDERR "All processes started\n";

我尝试了两种方式执行它,但结果都相同:
<16:34:21> rswhiting@Minas-Tirith:~/code/Pipes $ ./pipes 
All processes started
CTRL: Sun Apr 20 16:34:32 2014 VID: Sun Apr 20 16:34:22 2014 0
CTRL: Sun Apr 20 16:34:32 2014 VID: Sun Apr 20 16:34:23 2014 1
CTRL: Sun Apr 20 16:34:32 2014 VID: Sun Apr 20 16:34:24 2014 2
CTRL: Sun Apr 20 16:34:32 2014 VID: Sun Apr 20 16:34:25 2014 3
CTRL: Sun Apr 20 16:34:32 2014 VID: Sun Apr 20 16:34:26 2014 4
CTRL: Sun Apr 20 16:34:32 2014 VID: Sun Apr 20 16:34:27 2014 5
CTRL: Sun Apr 20 16:34:32 2014 VID: Sun Apr 20 16:34:28 2014 6
CTRL: Sun Apr 20 16:34:32 2014 VID: Sun Apr 20 16:34:29 2014 7
CTRL: Sun Apr 20 16:34:32 2014 VID: Sun Apr 20 16:34:30 2014 8
CTRL: Sun Apr 20 16:34:32 2014 VID: Sun Apr 20 16:34:31 2014 9
<16:34:49> rob@Minas-Tirith:~/code/Pipes $ ./video | ./controller 
CTRL: Sun Apr 20 16:35:25 2014 VID: Sun Apr 20 16:35:15 2014 0
CTRL: Sun Apr 20 16:35:25 2014 VID: Sun Apr 20 16:35:16 2014 1
CTRL: Sun Apr 20 16:35:25 2014 VID: Sun Apr 20 16:35:17 2014 2
CTRL: Sun Apr 20 16:35:25 2014 VID: Sun Apr 20 16:35:18 2014 3
CTRL: Sun Apr 20 16:35:25 2014 VID: Sun Apr 20 16:35:19 2014 4
CTRL: Sun Apr 20 16:35:25 2014 VID: Sun Apr 20 16:35:20 2014 5
CTRL: Sun Apr 20 16:35:25 2014 VID: Sun Apr 20 16:35:21 2014 6
CTRL: Sun Apr 20 16:35:25 2014 VID: Sun Apr 20 16:35:22 2014 7
CTRL: Sun Apr 20 16:35:25 2014 VID: Sun Apr 20 16:35:23 2014 8
CTRL: Sun Apr 20 16:35:25 2014 VID: Sun Apr 20 16:35:24 2014 9
<16:35:25> rob@Minas-Tirith:~/code/Pipes $ 

视频程序的时间戳按照应有的顺序递增,但控制程序接收到它们的时间是相同的(在视频程序结束后)。我该如何让控制器在数据生成时即时接收数据而非事后接收?
1个回答

4
这是由于video程序中的标准输出(STDOUT)缓冲机制导致的。只有当print命令输出到交互式终端时,它才是行缓冲的。否则,它会使用一个更大的缓冲区,并且在video退出时将累积的输出作为一大块输出。尝试在每次使用print命令后进行清空缓存或使用自动刷新功能。
$| = 1;  # Enables autoflush

顺便说一下,我不明白为什么你要打开一个指向video的管道,它并没有读取任何内容。应该改为:
system "./video";

如果您有一个不能修改但不自动刷新的程序,那么您就遇到了麻烦。但实际上并非如此。您可以使用伪终端而不是管道。我从未尝试过这样做,但书籍《UXIX编程接口》的第64章是关于伪终端的。

这真是太神奇了!有没有一种方法可以在视频程序外完成它?在完整的情况下,视频程序是一个编译后的 C++ 程序,输出坐标列表。如果不行的话,至少我知道问题叫什么名字了 :) - RobW
不,缓冲发生在“video”进程内的用户空间库中。但是,如果您可以欺骗程序输出到终端... - SzG
我的一个示例的先前迭代中,使用了exec "./video"而不是打开视频作为句柄。 - RobW
好的。在我的C++程序中,每当我完成一系列目标时,我只需要添加一个cout.flush()即可。不需要欺骗它并告诉它它是更大程序的一部分 :) - RobW
@RobW,如果它认为自己连接到终端,它就不会缓冲,因此您需要使用伪终端而不是管道。 - ikegami

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接