如何在Perl中异步运行系统命令?

Joe*_*oel 10 perl

我目前有一个Perl脚本,它在系统上运行外部命令,收集输出,并根据返回的内容执行一些操作.现在,这是我如何运行它(其中$ cmd是一个带有命令设置的字符串):

@output = `$cmd`;
Run Code Online (Sandbox Code Playgroud)

我想改变它,所以如果命令挂起并且在这么多时间之后没有返回值,那么我就杀了命令.我将如何异步运行此操作?

tse*_*see 12

有很多方法可以做到这一点:

  • 你可以用fork(perldoc -f fork)来做到这一点
  • 或使用线程(perldoc线程).这两个都使返回的信息难以传递回主程序.
  • 在支持它的系统上,您可以设置警报(perldoc -f警报),然后在信号处理程序中清理.
  • 您可以使用POE或Coro之类的事件循环.
  • 您可以使用open()或open2或open3(参见IPC :: Open2,IPC :: Open3)来启动程序,同时通过文件句柄获取STDOUT/STDERR,而不是反引号.对其运行非阻塞读取操作.(perldoc -f select和google"perl nonblocking read")
  • 作为openX()的更强大的变体,请查看IPC :: Run/IPC :: Cmd.
  • 可能是半夜我想​​不到的.

  • 在win32系统上注意断开的`select`,它只适用于套接字和可能与控制台窗口的怪异交互,wperl(以非控制台窗口方式运行perl,但对STDIO和子进程有奇怪的影响).从好的方面来说,你可以做一个`system(-1,'foo')`.此外,在运行64个子项后,某些启动进程的方法也会失败.IME,`Win32 :: Process :: Create()`是在bgrnd中运行程序最安全的方法. (3认同)

Nei*_*ely 8

如果你真的只需要在给定的系统调用上设置超时,这比异步编程要简单得多.

你需要的只是eval()块内的alarm().

下面是一个示例代码块,它将这些代码块放入一个可以放入代码中的子例程中.该示例调用sleep,因此输出并不令人兴奋,但确实显示了您感兴趣的超时功能.运行它的输出是:

/ bin/sleep 2 failure:在./time-out第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;
}
Run Code Online (Sandbox Code Playgroud)