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 的事情。你只是选了两个糟糕的例子。
type con
从控制台读取,而不是从 STDIN。这可以使用 type con <nul
.
查看
cat
极不寻常。在任一系统上,缓冲完全取决于单个应用程序,但几乎所有应用程序的工作方式都相同,并且与 cat
的工作方式不同。 cat
竭尽全力让这个场景发挥作用。
将 cat
替换为 perl -pe1
以查看几乎所有其他程序的行为:
1
first line: 1
<deadlock>
说服那些 "normal" 程序对其输出进行行缓冲而不是块缓冲的方法是创建伪 tty。例如,这就是 Expect 和 unbuffer
所做的。当然,这在 Windows 中不起作用。我不确定 Windows 程序是根据什么做出缓冲决定的,但我认为它不容易被伪造,因为我从未听说过这样做的方法。
我在工作中构建了一个简单的文本处理脚本,供另一个程序使用。当我完成后,有人记得脚本需要不阻塞 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 的事情。你只是选了两个糟糕的例子。
type con
从控制台读取,而不是从 STDIN。这可以使用type con <nul
. 查看
cat
极不寻常。在任一系统上,缓冲完全取决于单个应用程序,但几乎所有应用程序的工作方式都相同,并且与cat
的工作方式不同。cat
竭尽全力让这个场景发挥作用。
将 cat
替换为 perl -pe1
以查看几乎所有其他程序的行为:
1
first line: 1
<deadlock>
说服那些 "normal" 程序对其输出进行行缓冲而不是块缓冲的方法是创建伪 tty。例如,这就是 Expect 和 unbuffer
所做的。当然,这在 Windows 中不起作用。我不确定 Windows 程序是根据什么做出缓冲决定的,但我认为它不容易被伪造,因为我从未听说过这样做的方法。