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

sid*_*com 11 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";
Run Code Online (Sandbox Code Playgroud)

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

没有找到shell的绝对路径:&

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

Gra*_*ean 19

Perl的系统功能有两种模式:

  1. 获取单个字符串并将其传递给命令shell以允许处理特殊字符
  2. 获取字符串列表,执行第一个字符串并将剩余的字符串作为参数传递

在第一种形式中,您必须小心转义可能对shell具有特殊含义的字符.第二种形式通常更安全,因为参数直接传递给正在执行的程序而不涉及shell.

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

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

如果你更喜欢使用多参数形式,那么你需要调用fork()然后使用exec()而不是system().

  • **tldr;**`系统( "./ script.sh $ ARG1& ");`代替`系统(" ./ script.sh", "$ ARG1", "&");` (7认同)

bri*_*foy 12

请注意,系统的列表形式特别是处理诸如&shell元字符之类的字符.

perlfaq8的回答到如何在后台启动进程?


(由brian d foy提供)

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

几个CPAN模块可能能够提供帮助,包括IPC :: Open2IPC :: Open3,IPC :: Run,Parallel :: Jobs,Parallel :: ForkManager,POE,Proc :: BackgroundWin32 :: Process.您可能使用许多其他模块,因此请检查这些命名空间以获取其他选项.

如果您使用的是类Unix系统,那么您可以在系统调用中放弃,并在命令末尾添加&:

system("cmd &")
Run Code Online (Sandbox Code Playgroud)

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

STDIN,STDOUT和STDERR是共享的

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

植物大战僵尸

你必须准备在完成后"收获"子进程.

$SIG{CHLD} = sub { wait };

$SIG{CHLD} = 'IGNORE';
Run Code Online (Sandbox Code Playgroud)

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

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

有关执行此操作的其他代码示例,请参阅perlipc中的信号.僵尸不是系统的问题("prog&").