Como posso executar comandos do sistema Perl em segundo plano?

#!/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"; 

Eu tentei isso para executar o comando xterm em segundo plano, mas não funciona:

Nenhum caminho absoluto encontrado para o shell:

Qual seria o caminho certo para fazê-lo funcionar?

A function do sistema do Perl tem dois modos:

  1. pegar uma única string e passá-la ao shell de comando para permitir que caracteres especiais sejam processados
  2. pegando uma lista de strings, executando o primeiro e passando as strings restantes como argumentos

Na primeira forma, você deve ter cuidado para escaping de caracteres que possam ter um significado especial para o shell. A segunda forma é geralmente mais segura, já que os argumentos são passados ​​diretamente para o programa que está sendo executado sem o shell estar envolvido.

No seu caso, você parece estar misturando as duas formas. O caractere & só tem o significado de “iniciar este programa em segundo plano” se for passado para o shell. Em seu programa, o E comercial está sendo passado como o quinto argumento para o comando xterm.

Como Jakob Kruse disse, a resposta simples é usar a forma de cadeia única do system . Se algum dos argumentos veio de uma fonte não confiável, você teria que usar aspas ou fugir para torná-las seguras.

Se você preferir usar o formulário multi-argumento, precisará chamar fork () e provavelmente usar exec () em vez de system() .

Observe que o formulário de lista do sistema está especificamente lá para não tratar caracteres como & como meta-caracteres do shell.

Da resposta de perlfaq8 a Como faço para iniciar um processo em segundo plano?


(contribuído por brian d foy)

Não há uma maneira única de executar o código em segundo plano para que você não precise esperar que ele termine antes que o seu programa passe para outras tarefas. O gerenciamento de processos depende do seu sistema operacional em particular, e muitas das técnicas estão no perlipc.

Vários módulos CPAN podem ajudar, incluindo IPC :: Open2 ou IPC :: Open3 , IPC :: Executar , Paralelo :: Trabalhos , Paralelo :: ForkManager , POE , Proc :: Background e Win32 :: Process . Existem muitos outros módulos que você pode usar, portanto, verifique também esses namespaces para outras opções.

Se você estiver em um sistema parecido com o Unix, talvez consiga sair com uma chamada de sistema em que coloca & no final do comando:

 system("cmd &") 

Você também pode tentar usar fork, como descrito em perlfunc (embora seja a mesma coisa que muitos dos módulos farão por você).

STDIN, STDOUT e STDERR são compartilhados

Tanto o processo principal quanto o processo em segundo plano (o processo “filho”) compartilham os mesmos filehandles STDIN, STDOUT e STDERR. Se ambos tentarem acessá-los de uma vez, coisas estranhas podem acontecer. Você pode querer fechar ou reabri-los para a criança. Você pode contornar isso com a abertura de um pipe (consulte abrir em perlfunc), mas em alguns sistemas isso significa que o processo filho não pode sobreviver ao pai. Sinais Você terá que capturar o sinal SIGCHLD e possivelmente o SIGPIPE também. O SIGCHLD é enviado quando o processo em segundo plano é concluído. O SIGPIPE é enviado quando você escreve em um filehandle cujo processo filho foi fechado (um SIGPIPE não rastreado pode fazer com que seu programa morra silenciosamente). Isso não é um problema com o sistema (“cmd &”).

Zumbis

Você tem que estar preparado para “colher” o processo infantil quando terminar.

 $SIG{CHLD} = sub { wait }; $SIG{CHLD} = 'IGNORE'; 

Você também pode usar um garfo duplo. Você imediatamente espera () pelo primeiro filho, e o daemon de boot irá esperar () pelo seu neto assim que ele sair.

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

Veja Sinais no perlipc para outros exemplos de código para fazer isso. Zumbis não são um problema com o sistema (“prog”).

Você tentou?

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

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

Isso não é puramente uma explicação para Perl. O mesmo problema está em C e em outros idiomas.

Primeiro entenda o que o comando do sistema faz:

  1. Garfos
  2. Sob a chamada de processo filho exec
  3. O processo pai está aguardando que o processo filho bifurcado termine

Não importa se você passa vários argumentos ou um argumento. A diferença é que, com vários argumentos, o comando é executado diretamente. Com um argumento, o comando é empacotado pelo shell e, finalmente, executado como:

 /bin/sh -c your_command_with_redirections_and_ambersand 

Quando você passa um comando como some_command par1 par2 & , então entre o interpretador Perl e o comando é o processo sh ou bash usado como um wrapper, e ele está aguardando o término de some_command . Seu script está esperando pelo interpretador de shell, e nenhum waitpid adicional é necessário, porque o sistema de funções do Perl faz isso por você.

Quando você quiser implementar esse mecanismo diretamente no seu script, você deve:

  1. Use a function garfo . Veja o exemplo: http://users.telenet.be/bartl/classicperl/fork/all.html
  2. Sob a condição filho ( if ), use a function exec . Seu usuário é semelhante ao sistema , consulte o manual. Observe que exec faz com que o programa de processo filho / conteúdo / cobertura de dados seja executado pelo comando executado.
  3. Sob a condição pai (if, fork sai com diferente de zero), você usa waitpid, usando pid retornado pela function fork.

É por isso que você pode executar o processo em segundo plano. Eu espero que isso seja simples.

O exemplo mais simples:

 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 }