【问题标题】:How can I use Perl to start a Win32 program and redirect its output to stdout?如何使用 Perl 启动 Win32 程序并将其输出重定向到标准输出?
【发布时间】:2009-12-14 12:04:09
【问题描述】:

我有一个跨平台的 Perl 程序,它在 win 上启动一个 win32 windows 程序,在 mac 上启动一个 macosx 应用程序。

我用system(),在mac上把被调用程序的stdout写在Perl程序的stdout里,这就是我想要的。

在 Windows 上,似乎没有办法让标准输出成为 Windows 程序。因此,作为替代方案,我让程序写入日志文件,我希望 Perl 从日志文件中读取(因为它正在被写入,被调用的程序可以运行一个小时),并将其重定向回Perl的stdout,这样在Win和Mac上的体验是一样的。

有人知道如何在 Perl 中实际执行此操作吗?我在想:

  • fork 进程
  • File::Tail 日志文件,在数据进入时写入标准输出
  • 以某种方式确定程序何时真正完成运行。

我大概能弄清楚 #1 和 #2,但还不知道如何解决 #3。

【问题讨论】:

  • 根据我的经验,Windows 程序的行为应该符合您的期望:它的 STDOUT 应该去 Perl 脚本的 STDOUT 去的地方(除非 STDOUT 是绑定的文件句柄)
  • 确保您没有尝试使用 wperl.exe 运行它,否则它将无法工作。

标签: perl winapi stdout


【解决方案1】:

以下是否符合你的初衷:win32程序的stdout也是perlstdout(还是我误解了你原来的问题)?

// Win32 console application printing to stdout
#include <stdio.h>

int main(int argc, char* argv[])
{
    int idx;
    for (idx=0; idx < 10; idx++) {
        printf("Hello World!\n");
        fflush(stdout);
    }
    return 0;
}

Perl 程序捕获 stdout 的 windows 程序并重定向到 stdout

use strict; use warnings;

my $cmd = "Debug/hello.exe";
open my $cmd_h, "$cmd |" or die "Cannot open pipe to '$cmd': $!";
print "Perl:$_" while <$cmd_h>;
close $cmd_h or die "Cannot close pipe to '$cmd': $!";

【讨论】:

    【解决方案2】:

    如果您需要捕获程序打印到 STDOUT 的输出,为什么不简单地使用反引号而不是 system()

    my $stdout = `program_name`;
    if ( $? ) {
        print "Child process had an error";
    }
    

    【讨论】:

      【解决方案3】:

      IPC::Run3 允许您为标准输入提供内容并捕获标准输出和标准错误。

      【讨论】:

        【解决方案4】:

        您是否阅读过system 的文档? system 不允许你捕获 STDOUTqx 会。

        另一方面,您似乎并不想捕获STDOUT。您似乎希望程序的输出显示在与 Perl 程序相同的终端窗口中。如果您在 Windows 上调用的程序确实将其输出打印到 STDOUT 并且如果您在 cmd 窗口中运行您的 Perl 程序,那么这将自然发生。

        【讨论】:

          【解决方案5】:

          捕获fork调用的返回值,获取子进程ID。

          my $pid = fork();
          if ($pid == 0) { # child
              system("command > logfile");
              exit 0;
          }
          # else parent
          do {
              # ... apply File::Tail to logfile, print new output ... 
          while (process_is_active($pid));
          

          然后有很多方法可以告诉孩子何时完成。这里有两个:

          # 1. kill 0, $pid
          sub process_is_active {
              my ($pid) = @_;
              return kill 0, $pid;  # returns "number of processes successfully signalled"
          }
          

          有些系统在实现kill 0,... 方面比其他系统做得更好。

          # 2. non-blocking waitpid 
          sub process_is_active {
              use POSIX ':sys_wait_h';
              my ($pid) = @_;
              my $waitpid = waitpid $pid, WNOHANG;
              return $waitpid == $pid;
          }
          

          第二个解决方案只会工作一次——在waitpid 检测到进程已完成并获取它之后,如果您在该进程上再次调用它,它将返回-1

          【讨论】:

            【解决方案6】:

            不太清楚你所说的“获取标准输出”是什么意思,但是当从 Windows 命令行使用 perl.exe 运行时,这会正确地将“foo”打印到标准输出:

            perl -e "system 'echo foo'"
            

            所以我认为只要你在命令窗口中运行 Perl,并且在调用 system 之前不要试图弄乱任何标准输出文件句柄,它应该可以工作。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2020-03-07
              • 2013-04-30
              • 1970-01-01
              • 2012-08-15
              • 2010-12-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多