回答:
これがLinuxマシンで実行されていると仮定すると、私は常に次のように処理しました。
exec(sprintf("%s > %s 2>&1 & echo $! >> %s", $cmd, $outputfile, $pidfile));
これにより、コマンドが起動し$cmd
、コマンド出力がにリダイレクトされ$outputfile
、プロセスIDがに書き込まれ$pidfile
ます。
これにより、プロセスが実行していることと、まだ実行中かどうかを簡単に監視できます。
function isRunning($pid){
try{
$result = shell_exec(sprintf("ps %d", $pid));
if( count(preg_split("/\n/", $result)) > 2){
return true;
}
}catch(Exception $e){}
return false;
}
プロセスをサーバーサイドスクリプトとして任意の言語(php / bash / perl / etc)で記述し、phpスクリプトのプロセス制御関数から呼び出します。
関数はおそらく、標準ioが出力ストリームとして使用されているかどうかを検出し、それが使用されている場合は戻り値を設定します。そうでない場合は終了します
proc_close( proc_open( "./command --foo=1 &", array(), $foo ) );
私はコマンドラインから「sleep 25s」をコマンドとして使用してこれをすばやくテストしましたが、それは魅力のように機能しました。
これをコマンドに追加してみてください
>/dev/null 2>/dev/null &
例えば。
shell_exec('service named reload >/dev/null 2>/dev/null &');
Windowsでこの機能をテストするための非常に単純な例を追加したいと思います。
次の2つのファイルを作成し、Webディレクトリに保存します。
foreground.php:
<?php
ini_set("display_errors",1);
error_reporting(E_ALL);
echo "<pre>loading page</pre>";
function run_background_process()
{
file_put_contents("testprocesses.php","foreground start time = " . time() . "\n");
echo "<pre> foreground start time = " . time() . "</pre>";
// output from the command must be redirected to a file or another output stream
// http://ca.php.net/manual/en/function.exec.php
exec("php background.php > testoutput.php 2>&1 & echo $!", $output);
echo "<pre> foreground end time = " . time() . "</pre>";
file_put_contents("testprocesses.php","foreground end time = " . time() . "\n", FILE_APPEND);
return $output;
}
echo "<pre>calling run_background_process</pre>";
$output = run_background_process();
echo "<pre>output = "; print_r($output); echo "</pre>";
echo "<pre>end of page</pre>";
?>
background.php:
<?
file_put_contents("testprocesses.php","background start time = " . time() . "\n", FILE_APPEND);
sleep(10);
file_put_contents("testprocesses.php","background end time = " . time() . "\n", FILE_APPEND);
?>
上記のファイルを作成したディレクトリへの書き込み権限をIUSRに付与します
IUSRにC:\ Windows \ System32 \ cmd.exeの読み取りと実行を許可します。
Webブラウザーからforeground.phpにアクセスする
以下は、出力配列の現在のタイムスタンプとローカルリソース#を使用してブラウザーにレンダリングする必要があります。
loading page
calling run_background_process
foreground start time = 1266003600
foreground end time = 1266003600
output = Array
(
[0] => 15010
)
end of page
上記のファイルが保存されたのと同じディレクトリにtestoutput.phpが表示され、空であるはずです。
上記のファイルが保存されたのと同じディレクトリにtestprocesses.phpが表示され、次のテキストと現在のタイムスタンプが含まれているはずです。
foreground start time = 1266003600
foreground end time = 1266003600
background start time = 1266003600
background end time = 1266003610
PHPページが完了するのを待たずにバックグラウンドで何かを行う必要がある場合は、wgetコマンドで「呼び出される」別の(バックグラウンド)PHPスクリプトを使用できます。もちろん、このバックグラウンドPHPスクリプトは、システム上の他のPHPスクリプトと同様に、特権を使用して実行されます。
以下は、gnuwin32パッケージのwgetを使用したWindowsの例です。
例としてのバックグラウンドコード(ファイルtest-proc-bg.php)...
sleep(5); // some delay
file_put_contents('test.txt', date('Y-m-d/H:i:s.u')); // writes time in a file
フォアグラウンドスクリプト、呼び出し...
$proc_command = "wget.exe http://localhost/test-proc-bg.php -q -O - -b";
$proc = popen($proc_command, "r");
pclose($proc);
これが正しく機能するには、popen / pcloseを使用する必要があります。
wgetオプション:
-q keeps wget quiet.
-O - outputs to stdout.
-b works on background
PHPでバックグラウンドプロセスを起動する関数を次に示します。最後に、さまざまなアプローチとパラメーターを何度も読んでテストした後、実際にWindowsでも動作するものを作成しました。
function LaunchBackgroundProcess($command){
// Run command Asynchroniously (in a separate thread)
if(PHP_OS=='WINNT' || PHP_OS=='WIN32' || PHP_OS=='Windows'){
// Windows
$command = 'start "" '. $command;
} else {
// Linux/UNIX
$command = $command .' /dev/null &';
}
$handle = popen($command, 'r');
if($handle!==false){
pclose($handle);
return true;
} else {
return false;
}
}
注1:Windowsでは、/B
他の場所で提案されているパラメーターを使用しないでください。これstart
は、プロセスにコマンド自体と同じコンソールウィンドウを実行させるため、プロセスは同期的に処理されます。別のスレッドで(非同期に)プロセスを実行するには、を使用しないでください/B
。
注2:start ""
コマンドが引用符で囲まれたパスの場合、後に空の二重引用符が必要です。start
コマンドは、最初に引用されたパラメーターをウィンドウタイトルとして解釈します。
さて、私は使用する少し速くて簡単なバージョンを見つけました
shell_exec('screen -dmS $name_of_screen $command');
そしてそれは動作します。
別のプロセスを分岐して、バックグラウンドでコピーを実行するように手配できますか?PHPを実行してからしばらく経ちますが、関数pcntl-forkは有望に見えます。
この関数を使用して、プログラムをバックグラウンドで実行します。クロスプラットフォームで完全にカスタマイズ可能です。
<?php
function startBackgroundProcess(
$command,
$stdin = null,
$redirectStdout = null,
$redirectStderr = null,
$cwd = null,
$env = null,
$other_options = null
) {
$descriptorspec = array(
1 => is_string($redirectStdout) ? array('file', $redirectStdout, 'w') : array('pipe', 'w'),
2 => is_string($redirectStderr) ? array('file', $redirectStderr, 'w') : array('pipe', 'w'),
);
if (is_string($stdin)) {
$descriptorspec[0] = array('pipe', 'r');
}
$proc = proc_open($command, $descriptorspec, $pipes, $cwd, $env, $other_options);
if (!is_resource($proc)) {
throw new \Exception("Failed to start background process by command: $command");
}
if (is_string($stdin)) {
fwrite($pipes[0], $stdin);
fclose($pipes[0]);
}
if (!is_string($redirectStdout)) {
fclose($pipes[1]);
}
if (!is_string($redirectStderr)) {
fclose($pipes[2]);
}
return $proc;
}
コマンドの開始後、デフォルトでは、この関数は実行中のプロセスのstdinおよびstdoutを閉じます。$ redirectStdout引数と$ redirectStderr引数を使用して、プロセス出力をいくつかのファイルにリダイレクトできます。
Windowsユーザーへの注意:次の方法で
stdout / stderrをnul
にリダイレクトすることはできません。
startBackgroundProcess('ping yandex.com', null, 'nul', 'nul');
ただし、これは可能です。
startBackgroundProcess('ping yandex.com >nul 2>&1');
* nixユーザーへの注意:
1)実際のPIDを取得する場合は、execシェルコマンドを使用します。
$proc = startBackgroundProcess('exec ping yandex.com -c 15', null, '/dev/null', '/dev/null');
print_r(proc_get_status($proc));
2)プログラムの入力にデータを渡したい場合は、$ stdin引数を使用します。
startBackgroundProcess('cat > input.txt', "Hello world!\n");
WindowsとLinuxの両方の実用的なソリューション。詳細はMy github pageをご覧ください。
function run_process($cmd,$outputFile = '/dev/null', $append = false){
$pid=0;
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'This is a server using Windows!';
$cmd = 'wmic process call create "'.$cmd.'" | find "ProcessId"';
$handle = popen("start /B ". $cmd, "r");
$read = fread($handle, 200); //Read the output
$pid=substr($read,strpos($read,'=')+1);
$pid=substr($pid,0,strpos($pid,';') );
$pid = (int)$pid;
pclose($handle); //Close
}else{
$pid = (int)shell_exec(sprintf('%s %s %s 2>&1 & echo $!', $cmd, ($append) ? '>>' : '>', $outputFile));
}
return $pid;
}
function is_process_running($pid){
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'This is a server using Windows!';
//tasklist /FI "PID eq 6480"
$result = shell_exec('tasklist /FI "PID eq '.$pid.'"' );
if (count(preg_split("/\n/", $result)) > 0 && !preg_match('/No tasks/', $result)) {
return true;
}
}else{
$result = shell_exec(sprintf('ps %d 2>&1', $pid));
if (count(preg_split("/\n/", $result)) > 2 && !preg_match('/ERROR: Process ID out of range/', $result)) {
return true;
}
}
return false;
}
function stop_process($pid){
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'This is a server using Windows!';
$result = shell_exec('taskkill /PID '.$pid );
if (count(preg_split("/\n/", $result)) > 0 && !preg_match('/No tasks/', $result)) {
return true;
}
}else{
$result = shell_exec(sprintf('kill %d 2>&1', $pid));
if (!preg_match('/No such process/', $result)) {
return true;
}
}
}
この回答のおかげで:バックグラウンドプロセスを実行するのに最適なツールは、関数に基づくSymfonyプロセスコンポーネントですが、proc_*
はるかに使いやすくなっています。詳細については、ドキュメントを参照してください。
バックグラウンドプロセスを開始する代わりに、トリガーファイルを作成し、cronやautosysなどのスケジューラーに、トリガーファイルを探して操作するスクリプトを定期的に実行させるのはどうでしょうか。トリガーには、命令または生のコマンドを含めることができます(なお、シェルスクリプトにするだけです)。
PHPを使用している場合、pcntl_forkを使用してこれを行う簡単な方法があります。
私は頻繁に使用しています fast_cgi_finish_request()
クロージャーとregister_shutdown_function()と組み合わせて
$message ='job executed';
$backgroundJob = function() use ($message) {
//do some work here
echo $message;
}
次に、シャットダウンの前に実行されるこのクロージャーを登録します。
register_shutdown_function($backgroundJob);
最後に、応答がクライアントに送信されたら、クライアントへの接続を閉じて、PHPプロセスでの作業を続行できます。
fast_cgi_finish_request();
クロージャーはfast_cgi_finish_requestの後に実行されます。
$ messageはいつでも表示されません。また、必要な数のクロージャを登録できますが、スクリプトの実行時間に注意してください。これは、PHPがFast CGIモジュールとして実行されている場合にのみ機能します(そうでしたか?)
Windowsを使用している私たちにとっては、これを見てください:
リファレンス:http : //php.net/manual/en/function.exec.php#43917
スクリプトの実行を続けながら、プログラムをWindowsのバックグラウンドで実行することにも取り組みました。この方法では、他のソリューションとは異なり、最小化、最大化、またはウィンドウなしでプログラムを開始できます。llbra @ phpbrasilのソリューションは機能しますが、実際にタスクを非表示にしたい場合、デスクトップに不要なウィンドウが生成されることがあります。
Notepad.exeをバックグラウンドで最小化して起動します。
<?php
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("notepad.exe", 7, false);
?>
バックグラウンドで非表示のシェルコマンドを開始します。
<?php
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("cmd /C dir /S %windir%", 0, false);
?>
MSPaintを最大化して起動し、MSPaintを閉じるのを待ってからスクリプトを続行します。
<?php
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("mspaint.exe", 3, true);
?>
Run()メソッドの詳細については、http://msdn.microsoft.com/library/en-us/script56/html/wsMthRun.aspにアクセスして ください。
編集されたURL:
上記のリンクは存在しないため、代わりにhttps://technet.microsoft.com/en-us/library/ee156605.aspxにアクセスしてください。
Fatal error: Class 'COM' not found
私はそれが100年前の投稿であることを知っていますが、とにかく、誰かに役立つかもしれないと思いました。次のように、バックグラウンドで実行する必要のあるURLを指す非表示の画像をページのどこかに配置できます。
<img src="run-in-background.php" border="0" alt="" width="1" height="1" />