Answers:
假设它在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上测试此功能:
创建以下两个文件并将其保存到Web目录:
前景色.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
从网络浏览器中命中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脚本一样执行。
这是Windows上使用gnuwin32软件包中的wget的示例。
后台代码(文件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
命令将引用的第一个参数解释为窗口标题。
您是否可以安排一个单独的过程,然后在后台运行副本?自从我编写了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用户注意事项:
不能以nul
以下方式将stdout / stderr重定向到:
startBackgroundProcess('ping yandex.com', null, 'nul', 'nul');
但是,您可以这样做:
startBackgroundProcess('ping yandex.com >nul 2>&1');
* nix用户注意事项:
1)如果要获取实际的PID,请使用exec shell命令:
$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的有效解决方案。在我的github页面上找到更多信息。
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 Process Component,它是基于proc_*
函数的,但是使用起来更容易。有关更多信息,请参见其文档。
如果使用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之后执行。
$消息在任何时候都不可见。您可以根据需要注册任意数量的闭包,但要注意脚本执行时间。这仅在PHP作为快速CGI模块运行时才有效(是吗?!)
对于那些使用Windows的人,请看以下内容:
参考:http : //php.net/manual/zh/function.exec.php#43917
当脚本继续执行时,我也想让程序在Windows中在后台运行。与其他解决方案不同,此方法使您可以启动最小化,最大化或根本没有窗口的任何程序。llbra @ phpbrasil的解决方案确实有效,但是当您确实希望任务隐藏运行时,有时会在桌面上生成一个不需要的窗口。
在后台最小化启动Notepad.exe:
<?php
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("notepad.exe", 7, false);
?>
启动一个在后台不可见的shell命令:
<?php
$WshShell = new COM("WScript.Shell");
$oExec = $WshShell->Run("cmd /C dir /S %windir%", 0, false);
?>
启动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
编辑的网址:
请转到https://technet.microsoft.com/zh-cn/library/ee156605.aspx,因为上面的链接不再存在。
Fatal error: Class 'COM' not found
我知道这是一个已有100年历史的职位,但是无论如何,我认为这可能对某人有用。您可以在页面上的某处放置一个不可见的图像,以指向需要在后台运行的url,如下所示:
<img src="run-in-background.php" border="0" alt="" width="1" height="1" />