如何在后台运行Perl系统命令?

时间:2010-04-26 06:42:51

标签: perl background system

#!/usr/bin/env perl
use warnings; use strict;
use 5.012;
use IPC::System::Simple qw(system);

system( 'xterm', '-geometry', '80x25-5-5', '-bg', 'green', '&' );

say "Hello";
say "World";

我试过这个在后台运行xterm-command,但它不起作用:

  

找不到shell的绝对路径:&

使它运作的正确方法是什么?

4 个答案:

答案 0 :(得分:19)

Perl的system函数有两种模式:

  1. 获取单个字符串并将其传递给命令shell以允许处理特殊字符
  2. 获取字符串列表,执行第一个字符串并将剩余的字符串作为参数传递
  3. 在第一种形式中,您必须小心转义可能对shell有特殊含义的字符。第二种形式通常更安全,因为参数直接传递给正在执行的程序,而不涉及shell。

    在你的情况下,你似乎混合了两种形式。如果将&字符传递给shell,则其仅具有“在后台启动此程序”的含义。在你的程序中,&符号作为xterm命令的第5个参数传递。

    正如Jakob Kruse所说,简单的答案是使用system的单字符串形式。如果任何参数来自不受信任的来源,您必须使用引用或转义来使它们安全。

    如果您更喜欢使用多参数表单,则需要拨打fork(),然后使用exec()而不是system()

答案 1 :(得分:12)

请注意,system的列表形式特别针对而不是&等字符视为shell元字符。

perlfaq8回答How do I start a process in the background?


  

(由brian d foy提供)

     

没有一种方法可以在后台运行代码,因此在程序转移到其他任务之前,您无需等待它完成。流程管理取决于您的特定操作系统,许多技术都在perlipc中。

     

多个CPAN模块可能会提供帮助,包括IPC::Open2IPC::Open3IPC::RunParallel::JobsParallel::ForkManagerPOE,{ {3}}和Proc::Background。您可以使用许多其他模块,因此请检查这些名称空间以获取其他选项。

     

如果您使用的是类Unix系统,那么您可以通过系统调用来放弃一个&在命令的末尾:

system("cmd &")
     

您也可以尝试使用fork,如perlfunc中所述(尽管这与许多模块将为您做的事情相同)。

     

STDIN,STDOUT和STDERR是共享的

     

主进程和后台进程(“子进程”)共​​享相同的STDIN,STDOUT和STDERR文件句柄。如果两者都试图立即访问它们,可能会发生奇怪的事情。您可能想为孩子关闭或重新打开这些。您可以通过打开管道来解决这个问题(请参阅在perlfunc中打开),但在某些系统上,这意味着子进程不能超过父进程。   信号   你必须捕获SIGCHLD信号,也可能是SIGPIPE。在后台进程完成时发送SIGCHLD。当您写入子进程已关闭的文件句柄时,会发送SIGPIPE(未处理的SIGPIPE会导致程序无声地死亡)。这不是系统的问题(“cmd&”)。

     

僵尸

     

你必须准备在完成后“收获”子进程。

$SIG{CHLD} = sub { wait };

$SIG{CHLD} = 'IGNORE';
     

您也可以使用双叉。你马上等待()为你的第一个孩子,一旦它退出,init守护进程将为你的孙子等待()。

unless ($pid = fork) {
    unless (fork) {
        exec "what you really wanna do";
        die "exec failed!";
    }
    exit 0;
}
waitpid($pid, 0);
     

请参阅Win32::Process中的信号,了解执行此操作的其他代码示例。僵尸不是系统的问题(“prog&”)。

答案 2 :(得分:4)

你试过吗?

system('xterm -geometry 80x25-5-5 -bg green &');

http://www.rocketaware.com/perl/perlfaq8/How_do_I_start_a_process_in_the_.htm

答案 3 :(得分:0)

这不仅仅是对Perl的解释。同样的问题是在C和其他语言下。

首先了解系统命令的作用:

  1. 在子流程调用 exec
  2. 父进程正在等待分叉子进程完成
  3. 如果传递多个参数或一个参数,则无关紧要。不同之处在于,使用多个参数,命令将直接执行。使用一个参数,命令由shell包装,最后执行为:

    /bin/sh -c your_command_with_redirections_and_ambersand
    

    当您将命令作为 some_command par1 par2& 传递时,Perl解释器和命令之间是 sh bash 进程用作包装器,它正在等待 some_command 完成。您的脚本正在等待shell解释器,并且不需要额外的 waitpid ,因为Perl的函数 system 为您完成。

    如果要直接在脚本中实现此机制,则应该:

    1. 使用 fork 功能。请参阅示例:http://users.telenet.be/bartl/classicperl/fork/all.html
    2. 在子条件(if)下,使用exec功能。您的用户与系统类似,请参阅手册。注意,exec通过执行的命令导致子进程程序/内容/数据覆盖。
    3. 在父条件下(如果,fork以非零退出),使用waitpid,使用fork函数返回的pid。
    4. 这就是您可以在后台运行该过程的原因。我希望这很简单。

      最简单的例子:

      if (my $pid = fork) { #exits 0 = false for child process, at this point is brain split
        # parent ($pid is process id of child)
        # Do something what you want, asynchronously with executed command
        waitpid($pid);  # Wait until child ends
        # If you don't want to, don't wait. Your process ends, and then the child process will be relinked
        # from your script to INIT process, and finally INIT will assume the child finishing.
        # Alternatively, you can handle the SIGCHLD signal in your script
      }
      else {
        # Child
        exec('some_command arg1 arg2'); #or exec('some_command','arg1','arg2');
        #exit is not needed, because exec completely overwrites the process content
      }
      
相关问题