8

我在 Windows 上运行以下代码片段。服务器从客户端读取后开始连续监听。我想在一段时间后终止这个命令。

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

system()但是通过这种方式,我既不能在原始 Perl 文件中也不能在一个被调用的 Perl 文件中获取这个调用的输出。

任何人都可以让我知道终止system()呼叫或以我上面使用的方式获取输出的方式吗?

主文件

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

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

警报.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

4 回答 4

15

这里有几个单独的问题。

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

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

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

我最近遇到了一些问题alarmand system,但切换到 IPC::System::Simple解决了这个问题。

祝你好运, :)

于 2010-04-01T19:19:17.623 回答
9

我到底在想什么?此任务不需要后台进程。您只需要按照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 处理程序调用die.

于 2010-04-01T20:44:40.473 回答
2

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

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

在大量阅读 Perl IPC 和手动 fork & 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 );
}
于 2010-09-21T19:25:13.660 回答
-2

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

于 2010-04-01T23:10:09.100 回答