【问题标题】:How can I run a system command in Perl asynchronously?如何在 Perl 中异步运行系统命令?
【发布时间】:2010-12-17 15:57:09
【问题描述】:

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

@output = `$cmd`;

我想改变这个,所以如果命令挂起并且在很长时间后没有返回值,那么我会终止该命令。我将如何异步运行它?

【问题讨论】:

    标签: perl


    【解决方案1】:

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

    • 您可以使用 fork (perldoc -f fork) 完成此操作
    • 或使用线程(perldoc 线程)。这两者都使得将返回的信息传递回主程序变得困难。
    • 在支持它的系统上,您可以设置警报 (perldoc -f alarm),然后在信号处理程序中进行清理。
    • 您可以使用 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 个子进程后会失败。输入法,Win32::Process::Create() 是在 bgrnd 中运行程序的最安全方式。
    • @daotoad,感谢您的评论。我缺乏win32线索表明我唯一知道的是fork()是用线程模拟的,你不能使用警报。我想如果涉及win32的话,IPC::Cmd会是一个不错的选择。
    【解决方案2】:

    如果你真的只需要在给定的系统调用上设置一个超时时间,那是一个比异步编程简单得多的问题。

    你所需要的只是一个 eval() 块内的 alarm()。

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

    /bin/sleep 2 失败:超时 ./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;
    }
    

    【讨论】:

    【解决方案3】:

    如果您的外部程序不接受任何输入,请在 perlipc 手册页中查找以下字词:

    这是一个安全的反引号或管道打开以供阅读:

    使用示例代码并用警报保护它(perlipc 中也有解释)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-12
      • 1970-01-01
      • 2013-06-12
      • 2011-10-15
      • 2012-07-03
      • 2014-03-18
      • 2011-03-14
      • 1970-01-01
      相关资源
      最近更新 更多