ser*_*ixo 5 perl timeout waitpid
我正在编写一个需要在Windows和Linux中运行的perl脚本,它将运行一个进程,如果花费的时间太长,则将其超时,假设退出超时则返回退出代码,并假设exitcode为零且未退出则返回stdout超时。我不需要STDIN或STDERR。我尝试使用IPC::run但无法正常工作。
我最接近的是IPC::Open3和waitpid($pid, WNOHANG)。但是我遇到了障碍。我在Windows和Linux上看到了不同的结果。在下面的代码中,我给出open3了将失败的命令(ping没有任何参数-z)。在Linux上,代码立即返回负退出代码。在Windows上,命令超时。ping google.com -z在Windows命令行上运行会立即返回,告诉我没有这样的参数。为什么``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";
}
Run Code Online (Sandbox Code Playgroud)
欢迎其他解决方案。我将waitpid与nohang一起使用的唯一原因是,如果花费的时间太长,则将其杀死。在此期间,我没有其他需要做的处理。我在Windows上使用Windows 10 Strawberry Perl 5.28 Portable。我的debian机器具有perl 5.24。
IPC ::Run支持各种超时和计时器,这也应该适用于 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: $@";
}
Run Code Online (Sandbox Code Playgroud)
抛出timeout异常,因此eval. 还有其他计时器,其行为更加微妙且易于管理。请参阅文档。
如果捕获的异常不是由 's 计时器引起的,则重新抛出异常- 根据我系统上的模块版本在消息中打印的内容(以IPC::Run: timeoutIPC::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: $@";
}
};
Run Code Online (Sandbox Code Playgroud)
为了能够kill_kill在此处使用该模块,我首先创建了线束,然后在安装处理程序时可用该线束,并在kill_kill其触发时对其进行调用。
另一种方法是查找进程 ID(使用Win32::Process::Info或Win32::Process::List),然后使用kill、 或Win32::Process或终止它TASKKILL。请参阅这篇文章(后半部分)。
添加块只是为了local使信号处理程序化。在实际代码中,所有这些可能都以某种方式限定范围,因此可能不需要额外的块。
请注意,Windows 没有 POSIX 信号,Perl 仅模拟一些非常基本的 UNIX 信号以及特定于 Windows 的SIGBREAK.