Windows/Linux 子进程 STDIN 差异

Windows/Linux child process STDIN differences

我在工作中构建了一个简单的文本处理脚本,供另一个程序使用。当我完成后,有人记得脚本需要不阻塞 STDIN/STDOUT 才能使使用它的工具正常工作,并相应地修改了脚本。该脚本通过 IPC::Open2 在子进程中打开 *nix 的 cat 并将 STDIN 打印到它,读回它然后处理并将它打印到 STDOUT。我不知道这如何使脚本非阻塞,但它显然有效。

我希望它也能在 Windows 上工作,所以我将 cat 换成 type CON,这是一个用于打印 STDIN 的简单 Windows 命令。示例脚本如下:

use strict;
use warnings;
use IO::Handle;
use IPC::Open2;

my $command = ($^O eq 'MSWin32') ? 'type CON' : 'cat';

my ( $com_reader, $com_writer ) = ( IO::Handle->new, IO::Handle->new );
open2( $com_reader, $com_writer, $command );
# input
while (<STDIN>) {
    print "first line: $_";
    print $com_writer "$_";
    my $line = <$com_reader>;
    # ...process $line...

    print "next line: $line";
}

然而结果完全不同。在 Windows 上,主脚本和子脚本中的 STDIN 流似乎不同,而在 Linux 上它们是相同的。在 Windows(我在不同的输入行输入 1 和 2):

>perl test.pl
>1
first line: 1
>2
next line: 2
>1
>2
first line: 2
next line: 1
>1
>2
first line: 2
next line: 1
>1
>2
first line: 2
next line: 1

On Linux(相同输入):

>perl test.pl
>1
first line: 1
next line: 1
>2
first line: 2
next line: 2
>1
first line: 1
next line: 1
>2
first line: 2
next line: 2

为什么输出不同,如何使 Windows 行为与 Linux 行为匹配?另外,为什么这个 "open cat in subprocess and pipe input through it" 技巧完全有效?

这不是 Windows 和 Linux 的事情。你只是选了两个糟糕的例子。

  1. type con 从控制台读取,而不是从 STDIN。这可以使用 type con <nul.

  2. 查看
  3. cat 极不寻常。在任一系统上,缓冲完全取决于单个应用程序,但几乎所有应用程序的工作方式都相同,并且与 cat 的工作方式不同。 cat 竭尽全力让这个场景发挥作用。

cat 替换为 perl -pe1 以查看几乎所有其他程序的行为:

1
first line: 1
<deadlock>

说服那些 "normal" 程序对其输出进行行缓冲而不是块缓冲的方法是创建伪 tty。例如,这就是 Expect 和 unbuffer 所做的。当然,这在 Windows 中不起作用。我不确定 Windows 程序是根据什么做出缓冲决定的,但我认为它不容易被伪造,因为我从未听说过这样做的方法。