我正在编写一个需要在windows和linux中运行的perl脚本,该脚本将运行一个进程,如果时间过长,则超时,如果没有超时则返回退出代码,如果exitcode为零且没有超时则返回stdout。我不需要STDIN或STDERR。我试过使用IPC::run
,但无法使用。
我得到的最接近的是IPC::Open3
和waitpid($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 outn";
kill(9, $pid);
} else {
print "Exit Code: $exitcoden";
}
欢迎其他解决方案。我在nohang中使用waitpid的唯一原因是,如果花费太长时间,就会终止进程。在此期间,我没有任何其他需要处理的事情。我使用的是windows 10草莓perl 5.28可在windows上移植。我的debian机器有perl5.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: $@";
}
timeout
抛出异常,因此eval
抛出异常。还有其他计时器,其行为更加微妙和易于管理。请参阅文档。
如果捕获的异常不是由IPC::Run
的计时器引起的,则会重新引发异常——这取决于我的系统上模块的版本在消息中打印的内容,该消息是一个以IPC::Run:timeout开头的字符串。请检查您的系统上有什么。
虽然有些东西在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::进程::信息或Win32::过程::列表(,并使用kill
、Win32::Process或TASKKILL
终止它。请参阅本文(下半部分(。
一个块仅添加到local
中—使信号处理程序大小。在实际代码中,所有这些都可能以某种方式确定范围,因此可能不需要额外的块。
请注意,Windows没有POSIX信号,Perl只模拟一些非常基本的UNIX信号,以及特定于Windows的SIGBREAK
。