windows 和 linux 中的 Perl 超时命令

Perl timeout command in windows and linux

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

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

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.

IPC::Run has support for various 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 的计时器引起的,则会重新抛出异常——根据我系统上模块的版本在消息中打印的内容来判断,以 [= 开头的字符串47=]IPC::运行:超时。请检查您的系统上有什么。

虽然有些东西在 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 .

另一种方法是查找进程 ID(使用 Win32::Process::Info or Win32::Process::List), 并以 killWin32::Process, or TASKKILL. See (下半场)结束。

仅添加一个块以 local-ize 信号处理程序。在实际代码中,所有这些都可能以某种方式限定范围,因此可能不需要额外的块。

请注意 Windows 没有 POSIX 信号,Perl 仅模拟一些非常基本的 UNIX 信号,以及 Windows 特定的 SIGBREAK