exec shell assíncrona em PHP
-
03-07-2019 - |
Pergunta
Eu tenho um script PHP que precisa chamar um shell script, mas não se importa em tudo sobre a saída. O shell script faz uma série de chamadas SOAP e é lento para completar, então eu não quero abrandar o pedido PHP enquanto aguarda uma resposta. Na verdade, o pedido PHP deve ser capaz de sair sem que encerra o processo shell.
Eu olhei para os vários exec()
, shell_exec()
, pcntl_fork()
, etc. funções, mas nenhum deles parecem oferecer exatamente o que eu quero. (Ou, se o fizer, não é claro para mim como.) Alguma sugestão?
Solução
Se ele "não se preocupa com a saída", não poderia o exec para o script ser chamado com o &
a fundo o processo?
Editar - incorporando o @ AdamTheHut comentou a este post, você pode adicionar este a uma chamada para exec
:
" > /dev/null 2>/dev/null &"
Isso irá redirecionar tanto stdio
(primeira >
) e stderr
(2>
) para /dev/null
e correr em segundo plano.
Existem outras maneiras de fazer a mesma coisa, mas este é o mais simples de ler.
Uma alternativa para o acima double-redirect:
" &> /dev/null &"
Outras dicas
Eu costumava em para isso, como ele está realmente começando um processo independente.
<?php
`echo "the command"|at now`;
?>
Para todos os usuários do Windows: Eu encontrei uma boa maneira de executar um script PHP assíncrona (na verdade, ele funciona com quase tudo)
.É baseado em popen () e comandos pclose (). E funciona bem tanto em Windows e Unix.
function execInBackground($cmd) {
if (substr(php_uname(), 0, 7) == "Windows"){
pclose(popen("start /B ". $cmd, "r"));
}
else {
exec($cmd . " > /dev/null &");
}
}
código original de: http://php.net/manual/en/function.exec.php # 86329
No Linux, você pode fazer o seguinte:
$cmd = 'nohup nice -n 10 php -f php/file.php > log/file.log & printf "%u" $!';
$pid = shell_exec($cmd);
Isto irá executar o comando no prompty de comando e, em seguida, basta retornar o PID, que você pode verificar se há> 0 para garantir que ele trabalhou.
Esta questão é semelhante: O PHP tem rosqueamento
?php-execute-a-background-processo tem algumas boas sugestões . Eu acho que o meu é muito bom, mas eu sou preconceituoso:)
No Linux, você pode começar um processo em um novo segmento independente anexando um e comercial no final do comando
mycommand -someparam somevalue &
No Windows, você pode usar o "start" comando DOS
start mycommand -someparam somevalue
o caminho certo (!) Para fazê-lo é
- fork ()
- setsid ()
- execve ()
garfo, setsid dizer o atual processo para se tornar um mestre (sem pai), execve dizer o processo de chamada seja substituído pelo chamado. de modo que o pai pode sair sem afetar a criança.
$pid=pcntl_fork();
if($pid==0)
{
posix_setsid();
pcntl_exec($cmd,$args,$_ENV);
// child becomes the standalone detached process
}
// parent's stuff
exit();
Eu usei isso ...
/**
* Asynchronously execute/include a PHP file. Does not record the output of the file anywhere.
* Relies on the PHP_PATH config constant.
*
* @param string $filename file to execute
* @param string $options (optional) arguments to pass to file via the command line
*/
function asyncInclude($filename, $options = '') {
exec(PHP_PATH . " -f {$filename} {$options} >> /dev/null &");
}
(onde PHP_PATH
é um const definido como define('PHP_PATH', '/opt/bin/php5')
ou semelhante)
Ele passa em argumentos via linha de comando. Para lê-los em PHP, consulte argv .
A única maneira que eu achei que realmente funcionou para mim foi:
shell_exec('./myscript.php | at now & disown')
Eu também achei Symfony Processo Componente útil para isso.
use Symfony\Component\Process\Process;
$process = new Process('ls -lsa');
// ... run process in background
$process->start();
// ... do other things
// ... if you need to wait
$process->wait();
// ... do things after the process has finished
Veja como funciona em sua GitHub repo .
Use um chamado FIFO.
#!/bin/sh
mkfifo trigger
while true; do
read < trigger
long_running_task
done
Em seguida, sempre que você deseja iniciar a tarefa de longa duração, simplesmente escrever uma nova linha (não bloqueante para o arquivo gatilho.
Enquanto sua entrada é menor do que PIPE_BUF
e é uma única operação write()
, você pode escrever argumentos para o fifo e tê-los aparecer como $REPLY
no script.
Você também pode executar o script PHP como daemon ou cron : #!/usr/bin/php -q
sem fila de uso, você pode usar o proc_open()
como este :
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w") //here curaengine log all the info into stderror
);
$command = 'ping stackoverflow.com';
$process = proc_open($command, $descriptorspec, $pipes);