web-dev-qa-db-ja.com

PHPの非同期シェルexec

Shellスクリプトを呼び出す必要があるPHPスクリプトがありますが、出力はまったく気にしません。シェルスクリプトは多数のSOAP呼び出しを行い、完了するのに時間がかかるため、応答を待つ間、PHPリクエストを遅くしたくありません。実際、PHPリクエストは、シェルプロセスを終了せずに終了できるはずです。

私はさまざまなexec()Shell_exec()pcntl_fork()などの関数を調べましたが、どれも私が望むものを正確に提供していないようです。 (または、もしそうなら、それは私には明確ではありません。)提案はありますか?

192
AdamTheHutt

「出力を気にしない」場合、スクリプトのexecを&で呼び出してプロセスをバックグラウンドにできませんでしたか?

EDIT-この投稿にコメントされた@ AdamTheHut を組み込むと、これをexecの呼び出しに追加できます。

" > /dev/null 2>/dev/null &"

これにより、stdio(最初の>)とstderr2>)の両方が/dev/nullにリダイレクトされ、バックグラウンドで実行されます。

同じことを行う方法は他にもありますが、これが最も読みやすい方法です。


上記の二重リダイレクトの代替:

" &> /dev/null &"
210
warren

実際には独立したプロセスを開始しているため、このためにatを使用しました。

<?php
    `echo "the command"|at now`;
?>
52
Czimi

すべてのWindowsユーザーへ:非同期PHPスクリプトを実行する良い方法を見つけました(実際、ほとんどすべてで動作します)。

これは、popen()およびpclose()コマンドに基づいています。そして、WindowsとUnixの両方でうまく機能します。

function execInBackground($cmd) {
    if (substr(php_uname(), 0, 7) == "Windows"){
        pclose(popen("start /B ". $cmd, "r")); 
    }
    else {
        exec($cmd . " > /dev/null &");  
    }
} 

元のコード: http://php.net/manual/en/function.exec.php#86329

20
LucaM

Linuxでは、次のことができます。

$cmd = 'Nohup Nice -n 10 php -f php/file.php > log/file.log & printf "%u" $!';
$pid = Shell_exec($cmd);

これにより、コマンドプロンプトでコマンドが実行され、PIDが返されるだけです。PIDで> 0をチェックし、機能することを確認できます。

この質問は似ています: PHPにはスレッドがありますか?

19
Darryl Hein

php-execute-a-background-process にはいくつかの良い提案があります。私はかなり良いと思うが、私は偏っている:)

13
Mark Biek

Linuxでは、コマンドの最後にアンパサンドを追加することにより、新しい独立したスレッドでプロセスを開始できます

mycommand -someparam somevalue &

Windowsでは、DOSコマンド「開始」を使用できます

start mycommand -someparam somevalue
7
Leo

それを行う正しい方法(!)は

  1. フォーク()
  2. setsid()
  3. execve()

fork forks、setsidは現在のプロセスにマスタープロセス(親なし)になるように指示し、execveは呼び出しプロセスに呼び出されるプロセスに置き換えるように指示します。親が子に影響を与えることなく終了できるように。

 $pid=pcntl_fork();
 if($pid==0)
 {
   posix_setsid();
   pcntl_exec($cmd,$args,$_ENV);
   // child becomes the standalone detached process
 }

 // parent's stuff
 exit();
5
pierre_moon

私はこれを使用しました...

/** 
 * 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 &");
}

PHP_PATHdefine('PHP_PATH', '/opt/bin/php5')などのように定義されたconstです)

コマンドラインを介して引数を渡します。 PHPでそれらを読むには、 argv を参照してください。

4
philfreo

また、 Symfony Process Component がこれに役立つこともわかりました。

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

GitHub repo でどのように機能するかを確認してください。

3
Anton Pelykh

私にとって本当に役立つことがわかった唯一の方法は次のとおりです。

Shell_exec('./myscript.php | at now & disown')
3
Gordon Forsythe

PHPスクリプトをdaemonまたはcronjobとして実行することもできます:#!/usr/bin/php -q

1
Ronald Conco

名前付きfifoを使用します。

#!/bin/sh
mkfifo trigger
while true; do
    read < trigger
    long_running_task
done

その後、長時間実行されるタスクを開始したいときはいつでも、単純に改行(トリガーファイルへの非ブロッキング)を書き込みます。

入力がPIPE_BUFより小さく、単一のwrite()操作である限り、引数をfifoに書き込み、スクリプトで$REPLYとして表示させることができます。

1
geocar

キューを使用せずに、次のように proc_open() を使用できます。

    $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);
0
Kris Roofe