【问题标题】:Perl script to log external executables output and error while that still runsPerl 脚本记录外部可执行文件的输出和错误,同时仍然运行
【发布时间】:2018-06-19 14:55:22
【问题描述】:

我有一个 Perl 脚本,它运行一个外部可执行文件。该可执行文件运行一段时间(有时是几秒钟,有时是一个小时),可以将文本输出到 STDOUT 和 STDERR 以及退出代码,这些都是必需的。以下代码演示了第一个成功的外部可执行文件运行(带有一行的小 bash 脚本 - 注释),然后是错误的退出状态(例如 gs - ghostscript)。 我希望外部可执行文件将其 STDOUT 提供给 Perl 脚本以进行评估、过滤、格式化等,然后在外部仍在执行时将其记录到日志文件(也用于其他内容)。 STDERR 也可以以同样的方式工作。 该脚本可以记录来自 STDOUT 的所有内容,但仅在可执行文件完成之后。并且 STDERR 仅直接记录,没有评估等。我无法安装任何额外的 Perl 部件、模块等。

如何让我的 Perl 脚本从可执行文件中获取每一行 (STDOUT + STDERR),同时将其吐出(不仅仅是在结尾处)以及用于其他目的的退出代码?

#!/usr/bin/perl
@array_executable_and_parameters = "/home/username/perl/myexecutable.sh" ; #ls -lh ; for i in {1..5}; do echo X; sleep 1; done
@array_executable_and_parameters2= "gs aaa" ;
my $line;
chdir("/home/username/perl/");
$logFileName = "logfileforsomespecificinput.log";
open(LOGHANDLE, ">>$logFileName" );
open (STDERR, '>>', $logFileName);                  #Prints to logfile directly
#open (STDERR, '>>', <STDOUT>);                 #Prints to own STDOUT (screen or mailfile)

print LOGHANDLE "--------------OK run\n";
open CMD, '-|', @array_executable_and_parameters or die $@;
while (defined($line = <CMD>)) {                    #Logs all at once at end
    print LOGHANDLE "-----\$line=$line-----\n";
}
close CMD;
$returnCode1= $?>>8;
print LOGHANDLE "\$returnCode1=$returnCode1\n";

print LOGHANDLE "--------------BAD run\n";
open CMD2, '-|', @array_executable_and_parameters2 or die $@;
while (defined($line = <CMD2>)) {
    print LOGHANDLE "-----\$line=$line-----\n";
}
close CMD2;
$returnCode2= $?>>8;
print LOGHANDLE "\$returnCode2=$returnCode2\n";

close(LOGHANDLE);

采取 2。在 cmets 中得到很好的建议后,我尝试了 IPC::Run。但是有些事情仍然没有按预期工作。我似乎错过了从开始(或泵?)到完成的循环是如何工作的,以及当我不知道最后一个输出是什么时如何让它迭代——正如到处提到的例子。到目前为止,我现在有以下代码,但它不能逐行工作。它一口气吐出文件列表,然后等待外部循环完全完成以打印所有 X。如何驯服它以满足最初的需求?

#! /usr/bin/perl
use IPC::Run qw( start pump finish );

@array_executable_and_parameters = ();
push(@array_executable_and_parameters,"/home/username/perl/myexecutable.sh"); #ls -lh ; for i in {1..5}; do echo X; sleep 1; done
my $h = start \@array_executable_and_parameters, \undef, \$out, \$err ;
pump $h;# while ($out or $err);
print "1A. \$out: $out\n";
print "1A. \$err: $err\n";
$out = "";
$err = "";
finish $h or die "Command returned:\n\$?=$?\n\$@=$@\nKilled by=".( $? & 0x7F )."\nExit code=".( $? >> 8 )."\n" ;
print "1B. \$out: $out\n";
print "1B. \$err: $err\n";

【问题讨论】:

    标签: perl pipe stdout executable stderr


    【解决方案1】:

    查看IPC 模块,尤其是IPC::CmdIPC::Run,如果不满意则IPC::Run3。您必须涵盖很多细节,这些模块将使您的生活更轻松。

    【讨论】:

    • 虽然我同意包含一个简单示例会使这个答案更加有用,但审阅者请注意,这些链接指向推荐的模块的实际文档解决问题,而不是一些临时的博客文章等。metacpan 改变或消失的可能性要小得多。
    • @SinanÜnür:除了这些链接的波动性之外,还有很多原因可以避免仅使用链接的解决方案,这应该作为评论发布。完整的答案应包括对错误行为的解释,以及任何链接可能产生影响的原因。显然,最好还提供示例代码。
    • 是的,看起来很有希望。但是...我无法安装、使用外部代码、模块、依赖项等。难道没有一种简单的手动方式来实现相同的结果吗?
    • @uldics 你的意思是比重新发明轮子更简单?如果您知道open my $fh, '-|', ... 在内部只是对pipe()fork()close()dup()execl()execve() 等的调用,那么您就不必问了。但是您问,最好的建议是坚持推荐的模块。这不是一项简单的任务,并且有很多讨厌的错误的机会。
    • IPC::Cmd 无法交错 STDOUT 和 STDERR,IPC::Run3 可能会等到完成(不允许与子进程交互),所以从你的三个建议中我想只有一个能够满足我的要求是 IPC:运行。我如何让它按照我的要求执行?除了文件输出重定向之外,我在任何地方都看不到任何示例。在我的示例中,它可以做类似 open-pipe-while 的事情吗?
    【解决方案2】:

    好的,到目前为止,它已经开始工作了。可能有一些问题 - 不确定环境变量,如 umask 或语言相关或推送等待/阻塞时的系统负载,或者如何用捕获所有变量的状态替换 die。尽管如此,就我的目的而言,似乎运作良好。将看到它如何在真实系统上运行。

    #! /usr/bin/perl
    BEGIN {
        push @INC, '/home/myusername/perl5/lib/perl5';          #Where the modules from Cpan are
    }
    use IPC::Run qw( start pump finish );
    
    @array_executable_and_parameters = ();
    push(@array_executable_and_parameters,"/home/myusername/perl/myexecutable.sh"); #ls -lh ; for i in {1..5}; do echo X; sleep 1; done
    my $h = start \@array_executable_and_parameters, \undef, \$out, \$err ;
    while (42) {
        pump $h;# while ($out or $err);
        if ($out eq '' and $err eq '') {last;}
        print "1A. \$out: $out\n";
        print "1A. \$err: $err\n";
        $out = "";
        $err = "";
    }
    finish $h or die "Command returned:\n\$?=$?\n\$@=$@\nKilled by=".( $? & 0x7F )."\nExit code=".( $? >> 8 )."\n" ;
    print "1B. \$out: $out\n";
    print "1B. \$err: $err\n";
    

    关键是了解泵的阻塞是如何工作的。所有手册和帮助位置都跳过了这部分。因此,当泵在没有输出的情况下继续前进时跳出的无休止的时间是关键。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-08-21
      • 1970-01-01
      • 2021-06-15
      • 2020-08-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-15
      相关资源
      最近更新 更多