IPC::Open3 并确定孩子是不是正在等待输入

Posted

技术标签:

【中文标题】IPC::Open3 并确定孩子是不是正在等待输入【英文标题】:IPC::Open3 and determining if child is waiting for inputIPC::Open3 并确定孩子是否正在等待输入 【发布时间】:2017-01-04 00:54:29 【问题描述】:
sub run_command

    my $COMMAND         = shift;
    my @OUTPUT;
    my %CMD             = ;

    $CMDpid           = open3(my $CH_IN, my $CH_OUT, my $CH_ERR, $COMMAND);
    $CMD_STDIN        = $CH_IN;
    $CMD_STDOUT       = $CH_OUT;
    $CMD_STDERR       = $CH_ERR;

    my $line            = readline $CMD_STDOUT;
    print $line;

#    open my $CMDPROC, q-|, $COMMAND   or return;
#    foreach (<$CMDPROC>)
#    
#        push @OUTPUT, "$ARG";
#    
    close $CMDPROC                      or return;

    return @OUTPUT

上面的代码是我正在编写的脚本的一部分,它需要运行另一个脚本(称为子脚本)。孩子可能会或可能不会提示输入,这取决于 /var/tmp 中是否存在 cookie 文件(在 CentOS5 / perl 5.8.8 上编写的两个脚本)

我需要确定孩子是否以及何时等待输入,以便父母可以从父母的 STDIN 传递输入。我还需要使用 open3 打开子进程,因为我需要父进程通过 Perl::Critic 的残酷(严重性 1)检查。

我包含了 cmets,因为当 cookie 文件已经设置时,我至少可以让父级正确调用子级,因为在这种情况下子级不会等待输入。

我已经四处寻找有关如何确定孩子是否在等待输入的示例。我发现的一个例子使用了 strace (http://www.perlmonks.org/?node_id=964971),我觉得这对于我想做的事情来说可能太复杂了。

任何指导我的链接将不胜感激。

【问题讨论】:

您可以检查管道中是否有空间(使用select),但我从未听说过能够检查线程是否阻塞等待从管道读取。 子进程在从 STDIN 读取之前是否发出提示? 有什么理由不无条件通过输入? 除了没有任何理由之外,没有。有提示 那么只需提供输入而无需检查。不过,请使用 IPC::Run3 或 IPC::Run 而不是使用 IPC::Open3+IO::Select。后者太复杂了。 (如果你在这里没有使用selectopen3,你就做错了。)另一种选择是使用Expect 来驱动孩子,但这只是针线活。 【参考方案1】:

您可以检查管道中是否有空间(使用select)。您甚至可以检查管道中有多少可用空间。但是,我从来没有听说过检查线程是否被阻塞等待从管道读取的能力。我认为你应该探索其他途径。


在我看来,只有在满足与参数无关的某些条件时才从 STDIN 读取的程序会提供一个提示,表明它正在等待输入。如果是这种情况,可以使用Expect 来启动和控制子程序。

但最简单的解决方案是无条件地将数据写入 STDIN。使用 IPC::Open3 实现这个非常复杂[1],所以我建议切换到IPC::Run3(更简单)或IPC::Run(更灵活)。

# Capture's child's STDERR
run3 [ $prog, @args ], \$text_for_stdin, \my $text_from_stdout, \my $text_from_stderr;

# Inherits parent's STDERR
run3 [ $prog, @args ], \$text_for_stdin, \my $text_from_stdout;

    当您同时写入孩子的 STDIN 并从孩子的 STDOUT 读取时,您需要使用select(或其他东西)来避免死锁。 IPC::Open3 级别非常低,不会为您执行此操作,而处理此操作的是 IPC::Run3 和 IPC::Run raison d'être。

【讨论】:

谢谢!由于严格的安全要求,我不知道在这些系统上是否允许使用 expect,但 IPC::Run3 几乎肯定足以代替 IPC::Open3。一些附加信息和后续问题。子是另一个 perl 脚本,子脚本的唯一预期输入是密码,因为所有其他输入都在命令行上作为 GetOpt::Long 的选项提供。将 perl(作为父或子)尝试将数据发送到 STDIN 后立即处理,还是管道缓冲直到孩子真正开始等待输入? 我想run3run 会尽快将数据泵入管道。 是的,但是管道缓冲了吗?这才是真正的问题。如果它缓冲了,那么当孩子要求输入密码时,我需要显式地刷新缓冲区,这意味着我又回到了原点。如果它没有缓冲,那么我可能需要在父脚本中引入几毫秒的人为延迟,以便孩子有时间进入密码提示。 管道没有任何作用。它们不是代码。我已经回答了真正的问题。我的答案包括工作代码。 我最终要问的是:如果我在调用 run3 之后立即发送输入,但在子脚本提示输入密码之前,该输入是否仍会被孩子接收并用作密码,还是会简单地丢弃输入?我现在正在测试代码,但希望在尝试之前得到答案。

以上是关于IPC::Open3 并确定孩子是不是正在等待输入的主要内容,如果未能解决你的问题,请参考以下文章

如何检查使用 IPC::open3 执行的命令是不是挂起?

IPC::Open3 的更多麻烦 [重复]

IPC::Open3 在 Apache 下运行失败

perl IPC:Open3 最小通过 perlcritic?

为啥 IPC::Open3 调用 cmd.exe 而不是请求的程序?

Perl 行为差异关闭由 open() 产生的子进程与 IPC::Open3