perl - Windows 和 linux 中的 Perl 超时命令
问题描述
我正在编写一个 perl 脚本,该脚本需要在 windows 和 linux 中运行,它将运行一个进程,如果时间过长则超时,假设它没有超时返回退出代码,并假设退出代码为零并且它没有返回标准输出没有超时。我不需要 STDIN 或 STDERR。我试过使用IPC::run
但无法让它工作。
我得到的最接近的是IPC::Open3
and 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 out\n";
kill(9, $pid);
} else {
print "Exit Code: $exitcode\n";
}
欢迎其他解决方案。我使用带有 nohang 的 waitpid 的唯一原因是如果它花费的时间太长就终止该进程。在此期间,我没有任何其他需要做的处理。我在 windows 上使用 windows 10 草莓 perl 5.28 便携版。我的 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: $@";
}
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
这里使用模块,我首先创建了harness,然后在安装处理程序时可用,并且kill_kill
可以在它触发时调用它。
另一种方法是查找进程 ID(使用Win32::Process::Info或Win32::Process::List),并使用kill
、 或Win32::Process或TASKKILL
. 见这篇文章(下半部分)。
添加一个块仅用于local
-ize 信号处理程序。在实际代码中,所有这些都可能以某种方式限定范围,因此可能不需要额外的块。
请注意,Windows 没有 POSIX 信号,Perl 只模拟一些非常基本的 UNIX 信号,以及 Windows 特定的SIGBREAK
.
推荐阅读
- javascript - 如何使用 Masonry js 和 Rails 无休止的分页
- javascript - 在 Angular 组件中获取标签
- graphql - 为什么我的 graphQL 查询返回的结果没有 `where` 过滤器?
- python - Matplotlib x 轴动态范围更新
- woocommerce - woocomerce 订单状态更改后,请勿向客户发送电子邮件
- excel - 如何计算 Excel (Calc) 中的条纹,范围不断变化
- c# - 文件描述错误的 Openfiledialog 问题
- java - 在动态创建的卡片视图上,如果按下特定于卡片的删除按钮,如何删除单个卡片?
- bash - 在 Catalina 上使用 Anaconda 和 zsh
- r - 错误:“intsvy”的包或命名空间加载失败