Как запустить системную команду в Perl асинхронно?

в настоящее время у меня есть скрипт Perl, который запускает внешнюю команду в системе, собирает вывод и выполняет некоторые действия на основе того, что было возвращено. Прямо сейчас, вот как я запускаю это (где $cmd-строка с настройкой команды):

@output = `$cmd`;

Я хотел бы изменить это так, если команда зависает и не возвращает значение через столько времени, то я убиваю команду. Как я буду выполнять это асинхронно?

3 ответов


Если вам действительно нужно просто поставить тайм-аут на данный системный вызов, это гораздо проще, чем асинхронное программирование.

все, что вам нужно, это alarm() внутри блока eval ().

вот пример блока кода, который помещает их в подпрограмму, которую вы можете поместить в свой код. Пример вызывает sleep, поэтому не является захватывающим для вывода, но показывает вам функциональность тайм-аута, которая вас интересовала. Выход запуска:

/bin/sleep 2 сбой: тайм-аут при ./ линия тайм-аута 15.

$ cat time-out
#!/usr/bin/perl

use warnings;
use strict;
my $timeout = 1;
my @cmd = qw(/bin/sleep 2);
my $response = timeout_command($timeout, @cmd);
print "$response\n" if (defined $response);

sub timeout_command {
        my $timeout = (shift);
        my @command = @_;
        undef $@;
        my $return  = eval {
                local($SIG{ALRM}) = sub {die "timeout";};
                alarm($timeout);
                my $response;
                open(CMD, '-|', @command) || die "couldn't run @command: $!\n";
                while(<CMD>) {
                        $response .= $_;
                }
                close(CMD) || die "Couldn't close execution of @command: $!\n";
                $response;
        };
        alarm(0);
        if ($@) {
                warn "@cmd failure: $@\n";
        }
        return $return;
}

есть много способов сделать это:

  • вы можете сделать это с помощью вилки (вилка perldoc-f)
  • или использование потоков (потоки perldoc). Оба эти передачи возвращается обратно в основную программу сложно.
  • на системах, которые поддерживают его, вы можете установить сигнал тревоги (perldoc-F alarm), а затем очистить в обработчике сигнала.
  • вы можете использовать цикл событий, как POE или Coro.
  • вместо backticks, вы можете использовать open () или соответственно open2 или open3 (см. IPC::Open2, IPC:: Open3) для запуска программы при получении ее STDOUT/STDERR через дескриптор файла. Выполните неблокирующие операции чтения на нем. (perldoc-F выберите и, вероятно, google "perl nonblocking read")
  • как более мощный вариант openX (), проверьте IPC::Run/IPC::Cmd.
  • вероятно, тонны, о которых я не могу думать посреди ночи.

Если ваша внешняя программа не принимает никаких входных данных, найдите следующие слова в perlipc manpage:

вот безопасный щуп или труба открыта для чтения:

используйте пример кода и охраняйте его с помощью будильника (который также объясняется в perlipc).