Windows和Linux中的Perl超时命令

时间:2018-12-13 21:41:21

标签: perl timeout waitpid

我正在编写一个Perl脚本,该脚本需要在将运行进程的Windows和linux中工作,如果花费的时间太长则将其超时,并假设退出超时则返回退出代码,并假设exitcode为零时返回stdout。而且没有超时。我不需要STDIN或STDERR。我尝试使用IPC::run,但无法正常工作。

与我最接近的是IPC::Open3waitpid($pid, WNOHANG)。但是我遇到了障碍。我在Windows和Linux上看到了不同的结果。在下面的代码中,我给open3一个将失败的命令(ping没有任何参数-z)。在Linux上,代码立即返回负退出代码。在Windows上,命令超时。在Windows命令行上运行ping google.com -z会立即返回,告诉我没有这样的参数。为什么``waitpid''返回零?

use strict;
use warnings;
use POSIX ":sys_wait_h";
use IPC::Open3;

my $inFH;
my $outFH;
my @cmd = ("ping", "google.com", "-z");
my $pid = open3( $inFH, $outFH, 0, @cmd);
my $counter=0;
my $waitret;
my $exitcode;
do{
    $counter++;
    $waitret = waitpid($pid,WNOHANG);   
    $exitcode = $?;
}while( !$waitret and ($counter < 4_000_000));

if ($counter >= 4_000_000) {
    print "Command timed out\n";
    kill(9, $pid);
} else {
    print "Exit Code: $exitcode\n";
}

欢迎其他解决方案。我将waitpid与nohang一起使用的唯一原因是,如果花费的时间太长,则将其杀死。在此期间,我没有其他需要做的处理。 我在Windows上使用Windows 10 Strawberry Perl 5.28 Portable。我的debian机器安装了perl 5.24。

1 个答案:

答案 0 :(得分:3)

IPC::Run支持各种timeouts and timers,它们也可以在Win32上运行。

基本:

use warnings;
use strict;
use feature 'say';

use IPC::Run qw(run timeout);

my $out;

eval {
    run [ qw(sleep 20) ], \undef, \$out, timeout(2) or die "Can't run: $?"
};  
if ($@) { 
    die $@ if $@ !~ /^IPC::Run: timeout/;
    say "Eval: $@";
}

timeout引发异常,因此eval。还有其他计时器,其行为更加微妙和易于管理。请参阅文档。

如果捕获的异常不是由IPC::Run的计时器引起的,则重新引发异常-根据消息中输出的系统上模块的版本判断,该字符串以 IPC开头:: Run:超时。请检查系统上的内容。

我认为,尽管某些事情在Windows上不起作用,但计时器应该可以。我现在无法测试。


据报道,尽管上面的方法有效,但使用了更有意义的命令,SIGBREAK (21)会在超时时发出,一旦处理,该过程就会继续进行。

在这种情况下,请在处理程序中手动将其终止

use warnings;
use strict;
use feature 'say';
use IPC::Run qw(run harness timeout);

my $out;
my @cmd = qw(sleep 20);

my $h = harness \@cmd, \undef, \$out, timeout(2);

HANDLE_RUN: {
    local $SIG{BREAK} = sub {
        say "Got $_[0]. Terminate IPC::Run's process";
        $h->kill_kill;
    };  

    eval { run $h };  
    if ($@) { 
        die $@ if $@ !~ /^IPC::Run: timeout/;
        say "Eval: $@";
    }
};

为了能够在此处使用模块的kill_kill,我首先创建一个线束,该线束在安装处理程序时可用,并且在触发时可以在其上调用kill_kill。 / p>

另一种方法是查找进程ID(使用Win32::Process::InfoWin32::Process::List), 并以killWin32::ProcessTASKKILL终止。参见this post(下半部分)。

仅将一个块添加到(有意义地)local大小的信号处理程序中。在实际代码中,所有这些可能都以某种方式确定了范围,因此可能不需要额外的块。

请注意,Windows没有POSIX信号,Perl仅仿真一些非常基本的UNIX信号,以及Windows专用的SIGBREAK