【问题标题】:Can't get system command to timeout in perl script无法让系统命令在 perl 脚本中超时
【发布时间】:2014-01-20 22:23:45
【问题描述】:

我浏览并阅读了其他一些关于在一段时间后让命令停止运行的问题,但我尝试的一切都不起作用。

该命令的行为就像它想在指定的时间后停止一样(它返回终端提示符一秒钟)但只是继续运行并且不会停止。

这是我的代码:

#!/usr/bin/perl

use strict;
use warnings;


use Try::Tiny;

try {
        local $SIG{ALRM} = sub { die "alarm\n" };
        alarm 5;
        system("ping 192.168.1.1");
        alarm 0;
}
catch {
        die $_ unless $_ eq "alarm\n";
        print "timed out\n";
};
exit;

终端中的输出如下所示:

PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=1.98 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=3.13 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=3.20 ms
64 bytes from 192.168.1.1: icmp_seq=4 ttl=64 time=3.17 ms
64 bytes from 192.168.1.1: icmp_seq=5 ttl=64 time=3.16 ms
64 bytes from 192.168.1.1: icmp_seq=6 ttl=64 time=3.11 ms
64 bytes from 192.168.1.1: icmp_seq=7 ttl=64 time=3.18 ms
64 bytes from 192.168.1.1: icmp_seq=8 ttl=64 time=3.20 ms
64 bytes from 192.168.1.1: icmp_seq=9 ttl=64 time=3.22 ms
64 bytes from 192.168.1.1: icmp_seq=10 ttl=64 time=3.20 ms
skilo@hp-xubuntu:~/perlstuff$ 64 bytes from 192.168.1.1: icmp_seq=11 ttl=64 time=3.06 ms
64 bytes from 192.168.1.1: icmp_seq=12 ttl=64 time=3.19 ms
64 bytes from 192.168.1.1: icmp_seq=13 ttl=64 time=3.21 ms
64 bytes from 192.168.1.1: icmp_seq=14 ttl=64 time=3.21 ms

注意它如何试图停止但不能。

【问题讨论】:

  • 您是否在catch 块中打印了$_
  • 它会在 Windows 上为我打印 timed out\n,但之后会继续 ping。很有趣。
  • 是的,它只是在提示上方打印“警报”一秒钟然后继续。
  • stackoverflow.com/a/3763712/1331451 表示这个问题是已知的,也给出了解决方案。

标签: perl


【解决方案1】:

perl 'system' 内置函数 fork 你的进程,并在 fork 创建的子进程中执行 'ping' 程序。然后,父进程等待子进程完成。

您的警报计时器和信号处理程序中断等待进程,然后终止父进程,让子进程在后台运行。

你需要做的是:

  1. 警报超时后,通过发送信号终止子进程
  2. 您仍应(手动)等待该过程,以避免出现僵尸。

试试这个:

#!/usr/bin/perl

use strict;
use warnings;



eval {
        local $SIG{ALRM} = sub { die "alarm\n" };
        alarm 5;
        system("ping -v 192.168.1.1");
        alarm 0;
};
if ($@) {
        die $@ unless $@ eq "alarm\n";
        print "timed out\n";
        kill 2, -$$;
        wait;
};
exit;

注意:我的系统上没有 Try::Tiny,所以我用老式的 eval 块替换了它。但它应该工作相同。

这个“kill”命令需要一个信号号(我用 2 表示 SIGINT,相当于按 ctrl-c)和一个或多个要杀死的进程。 $$ 是当前进程的pid;负值具有杀死与当前进程关联的整个进程组的效果(这实际上意味着所有子进程)。请注意,这不是完全可移植的——如果您发现它在您的系统上不起作用,那么您将需要能够找到子进程的实际 PID。为了能够做到这一点,您应该将 system 替换为 fork 然后 exec 的调用,如下所示:

#!/usr/bin/perl

use strict;
use warnings;


my $childPid;
eval {
        local $SIG{ALRM} = sub { die "alarm\n" };
        alarm 5;
        if ($childPid = fork()) {
                wait();
        } else {
                exec("ping -v 192.168.1.1");
        }
        alarm 0;
};
if ($@) {
        die $@ unless $@ eq "alarm\n";
        print "timed out\n";
        kill 2, $childPid;
        wait;
};
exit;

【讨论】:

  • 这在 Windows cmd 上对我不起作用,只需尝试“超时 10”作为命令。它打印“终止”并返回提示,但子进程继续运行和打印,弄乱了 cmd。知道为什么吗?
猜你喜欢
  • 2017-03-15
  • 2013-09-17
  • 2013-06-12
  • 1970-01-01
  • 2014-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多