【问题标题】:Windows/Linux child process STDIN differencesWindows/Linux 子进程 STDIN 差异
【发布时间】:2023-11-24 13:11:01
【问题描述】:

我在工作中构建了一个简单的文本处理脚本,供另一个程序使用。当我完成后,有人记得脚本不需要阻止 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

在 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 行为匹配?另外,为什么这种“在子进程中打开猫并通过它管道输入”的技巧完全有效?

【问题讨论】:

  • @Harry Johnston,与子进程的双向交互通信。
  • @harry Johnston,实际上,这将是一个更复杂的程序。
  • @Nate:顺便说一下,所写的脚本行为异常的原因是脚本和子进程正在竞争相同的输入源。
  • @Harry Johnston,他们不应该使用相同的输入源。即使你解决了这个问题,他仍然会遇到问题。
  • Nate,我最好的猜测是,将数据通过管道传输到 cat 并返回是完全没有必要的,要使脚本正常工作,您真正需要做的就是通过说 $| = 1 来禁用缓冲;如果是这样,那应该适用于 Linux 和 Windows。如果cat 真的在做一些神奇的事情来解决一个真正的问题,那么您将需要确切地弄清楚问题是什么以及为什么cat 解决了它;我们需要更多信息来帮助您。首先向我们展示您的原始脚本是什么样子(在您的同事“修复”它之前)并告诉我们您使用它时出了什么问题。

标签: linux windows perl process stdin


【解决方案1】:

这不是 Windows 与 Linux 的区别。你只是选择了两个糟糕的例子。

  1. type con 从控制台读取,而不是从 STDIN 读取。这可以使用type con &lt;nul 看到。

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

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

1
first line: 1
<deadlock>

说服那些“正常”程序对它们的输出进行行缓冲而不是块缓冲的方法是创建一个伪 tty。例如,这就是 Expect 和 unbuffer 所做的。当然,这在 Windows 中是行不通的。我不确定 Windows 程序决定缓冲的依据是什么,但我认为它不容易被伪造,因为我从未听说过这样做的方法。

【讨论】:

  • 为什么perl -pe1会导致死锁?此外,type CON 会重复我在 cmd 中输入的任何内容,我认为这是 STDIN。它在读什么?
  • @Nate: CON 是控制台输入,即键盘。
  • &lt;$com_reader&gt; 阻塞,直到收到来自子进程的换行符,但 child 不会打印任何内容,直到它有 4k(Perl 的旧默认值)或 8k(Perl 的新默认值)要打印的数据,它只能从其父级获取,但其父级不会再发送任何数据,因为它已被阻止。