在perl脚本中无法使系统命令超时

时间:2014-01-20 22:23:46

标签: perl

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

该命令的行为就像它想要在指定的时间后停止(它会在短时间内返回终端提示)但只是继续前进而不会停止。

这是我的代码:

#!/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

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

1 个答案:

答案 0 :(得分:4)

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

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

您需要做的是:

  1. 警报超时后,通过发送信号来杀死子进程
  2. 你应该(手动)等待这个过程,以避免僵尸。
  3. 试试这个:

    #!/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。为了能够做到这一点,你应该用对fork和exec的调用替换system,如下所示:

    #!/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;