如何在Perl中终止带有警报的系统命令?

时间:2010-04-01 19:06:54

标签: perl system alarm

我在Windows上运行以下代码段。从客户端读取后,服务器开始连续监听。我希望在一段时间后终止此​​命令。

如果我在main.pl中使用alarm()函数调用,那么它会终止整个Perl程序(此处为main.pl),因此我将此system命令置于其中一个单独的Perl文件 并使用system命令在原始Perl文件中调用此Perl文件(alarm.pl)。

但是通过这种方式,我无法在原始Perl文件中调用此system()调用的输出,也无法在调用的一个Perl文件中调用。

有人可以让我知道终止system()电话的方式,还是以我上面使用过的方式获取输出?

main.pl

my @output = system("alarm.pl");
print"one iperf completed\n";

open FILE, ">display.txt" or die $!; 
print FILE @output_1; 
close FILE;

alarm.pl

alarm 30;
my @output_1 = readpipe("adb shell cd /data/app; ./iperf -u -s -p 5001");

open FILE, ">display.txt" or die $!; 
print FILE @output_1; 
close FILE;

两种方式display.txt始终为空。

4 个答案:

答案 0 :(得分:15)

这里有一些单独的问题。

首先,为了防止alarm杀死你的脚本,你需要处理ALRM信号。请参阅alarm文档。你不应该需要两个脚本。

其次,system不捕获输出。如果你想这样做,你需要一个反引号变体或管道。 Stackoverflow已经有了答案。

第三,如果 alarm.pl 将任何内容放入 display.txt 中,则在重新打开时将其丢弃在 main.pl 中文件处于写入模式。您只需要在一个地方创建文件。当你摆脱额外的脚本时,你就不会遇到这个问题。

我最近遇到了alarmsystem的一些问题,但是switching to IPC::System::Simple解决了这个问题。

祝你好运,:))

答案 1 :(得分:8)

我到底在想什么?您不需要此任务的后台进程。您只需按照perldoc -f alarm函数中的示例操作,并将时间敏感的代码包装在eval块中。

my $command = "adb shell cd /data/app; ./iperf -u -s -p 5001";
my @output;
eval {
    local $SIG{ALRM} = sub { die "Timeout\n" };
    alarm 30;
    @output = `$command`;
    alarm 0;
};
if ($@) {
    warn "$command timed out.\n";
} else {
    print "$command successful. Output was:\n", @output;
}

eval区块内,您可以常规方式捕获输出(使用反引号或qx()readpipe)。虽然如果通话超时,也不会有任何输出。

如果您不需要输出(或者不介意一起黑客攻击某些进程间通信),那么几乎可以防止出现警报并在子进程中运行system调用过程

<击>
$command = "adb shell cd /data/app; ./iperf -u -s -p 5001";
if (($pid = fork()) == 0) {
    # child process
    $SIG{ALRM} = sub { die "Timeout\n" }; # handling SIGALRM in child is optional
    alarm 30;
    my $c = system($command);
    alarm 0;
    exit $c >> 8;  # if you want to capture the exit status
}
# parent
waitpid $pid, 0;
当孩子的警报响起并杀死孩子时,当孩子的waitpid命令完成,时,

system将返回。对于未处理的SIGALRM,$?将保留系统调用的退出代码或其他内容(在我的系统上为142),如果SIGALRM处理程序调用{​​{1}},则保留255.

答案 2 :(得分:2)

我遇到了类似的问题,需要:

  • 运行系统命令并获取其输出
  • x秒后超时系统命令
  • 终止系统命令进程和所有子进程

经过多次阅读Perl IPC和手动前叉和手册exec,我出来了这个解决方案。它被实现为模拟的“反引号”子例程。

use Error qw(:try);

$SIG{ALRM} = sub {
    my $sig_name = shift;
    die "Timeout by signal [$sig_name]\n";
};

# example
my $command = "vmstat 1 1000000";
my $output = backtick( 
                 command => $command, 
                 timeout => 60, 
                 verbose => 0 
             );

sub backtick {

    my %arg = (
        command => undef,
        timeout => 900,
        verbose => 1,
        @_,
    );

    my @output;

    defined( my $pid = open( KID, "-|" ) )
        or die "Can't fork: $!\n";

    if ($pid) {

        # parent

        # print "parent: child pid [$pid]\n" if $arg{verbose};

        try {
            alarm( $arg{timeout} );
            while (<KID>) {
                chomp;
                push @output, $_;
            }

            alarm(0);
        }
        catch Error with {
            my $err = shift;
            print $err->{-text} . "\n";

            print "Killing child process [$pid] ...\n" if $arg{verbose};
            kill -9, $pid;
            print "Killed\n" if $arg{verbose};

            alarm(0);
        }
        finally {};
    }
    else {

        # child

        # set the child process to be a group leader, so that
        # kill -9 will kill it and all its descendents
        setpgrp( 0, 0 );

        # print "child: pid [$pid]\n" if $arg{verbose};
        exec $arg{command};
        exit;
    }

    wantarray ? @output : join( "\n", @output );
}

答案 3 :(得分:-2)

如果您的命令在您的系统上已经很常见,可以使用“timeout -n”来包装命令。