2017-02-14 100 views
0

我想在PHP中异步执行shell命令。即PHP不应该等待命令完成才能继续执行。然而,与Stackoverflow上关于该主题的大量问题相比,我确实关心程序的输出。我特别希望做这样的事情:PHP异步执行shell命令并检索实时输出

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会立即返回,同时还添加元素的数组,如果有检查的过程中已经终止或者没有一种方法。

有没有办法做到这一点?

+0

https://github.com/clue/php-shell-react或类似 –

+0

您可以生成一个线程,做并检查该线程的状态。可能有很多资源可以检查。但是你不能用原生PHP做这件事,并且需要一个模块或库。 – apokryfos

+0

@apokryfos [proc_open](http://php.net/manual/en/function.proc-open.php)是一个非常原生的PHP函数。 Libs使它更方便地使用它。 –

回答

0

我已经解决了输出STDIN到一个临时文件然后从中读取的问题。

这里是我的

实施

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); 
    } 
}