PHP异步执行shell命令并检索实时输出
我想在PHP中异步执行Shell命令. IE. PHP不应该等待命令完成才能继续执行.但是,与Stackoverflow上有关该主题的众多问题相反,我确实关心程序的输出.特别是我想做这样的事情:
I'd like to execute a shell command asynchronously in PHP. I.e. PHP shouldn't wait for the command to be finished to continue execution. However in contrast to the numerous question about that topic on Stackoverflow I do care about the output of the program. In particular I would like to do something like this:
exec("some command", $output_array, $has_finished);
while(count($output_array) > 0 && !$has_finished)
{
if(count($output_array) > 0)
{
$line = array_shift($output_array);
do_something_with_that($line);
} else
sleep(1);
}
do_something_with_that($line)
{
echo $line."\n";
flush();
}
如果exec
在仍向数组中添加元素的同时立即返回,并且有一种方法可以检查进程是否终止,则上述代码将起作用.
The above code would work if exec
would return immediately while still adding elements to the array and if there was a method to check if the process has terminated or not.
有办法吗?
我已通过将输出STDIN管道传输到临时文件然后从中读取来解决了这个问题.
I've solved the problem by piping the output STDIN to a temporary file and then reading from it.
这是我的
class ExecAsync {
public function __construct($cmd) {
$this->cmd = $cmd;
$this->cacheFile = ".cache-pipe-".uniqid();
$this->lineNumber = 0;
}
public function getLine() {
$file = new SplFileObject($this->cacheFile);
$file->seek($this->lineNumber);
if($file->valid())
{
$this->lineNumber++;
$current = $file->current();
return $current;
} else
return NULL;
}
public function hasFinished() {
if(file_exists(".status-".$this->cacheFile) ||
(!file_exists(".status-".$this->cacheFile) && !file_exists($this->cacheFile)))
{
unlink($this->cacheFile);
unlink(".status-".$this->cacheFile);
$this->lineNumber = 0;
return TRUE;
} else
return FALSE;
}
public function run() {
if($this->cmd) {
$out = exec('{ '.$this->cmd." > ".$this->cacheFile." && echo finished > .status-".$this->cacheFile.";} > /dev/null 2>/dev/null &");
}
}
}
用法
$command = new ExecAsync("command to execute");
//run the command
$command->run();
/*We want to read from the command output as long as
*there are still lines left to read
*and the command hasn't finished yet
*if getLine returns NULL it means that we have caught up
*and there are no more lines left to read
*/
while(($line = $command->getLine()) || !$command->hasFinished())
{
if($line !== NULL)
{
echo $line."\n";
flush();
} else
{
usleep(10);
}
}