背景:我正在尝试使用php编写一个shell脚本,该脚本将自动签出几个大型SVN repo。我还使用PEAR控制台进度条类来显示签出的进度(这不是完全必要的,但这正是我提出问题的原因(。
问题:有没有一种方法可以运行一个循环,它会随着命令行上STDIN的每一行输出而更新?
如果我做
<?php shell_exec("svn co svn://my.repo.com/repo/trunk"); ?>
我用一个巨大的字符串返回命令的所有输出。尝试类似的循环
<?php $bar = new Console_ProgressBar('%fraction% [%bar%] %percent% | %elapsed% :: %estimate%', '=>', ' ', 80, $total);
$bar->display(0);
stream_set_blocking(STDIN, 0);
$output = array();
$return = '';
exec("svn co $svnUrl $folder", $output, $return);
while (!isset($return))
{
$bar->update(count($output));
}
$bar->erase(); ?>
将显示栏,但不会更新。
有什么解决方案吗?
==========================更新=========================
以下是我根据答案得出的工作代码(供将来参考(:
<?php
$bar = new Console_ProgressBar('%fraction% [%bar%] %percent% | %elapsed% :: %estimate%', '=>', ' ', 80, $total);
$handle = popen("/usr/bin/svn co $svnUrl $folder", 'r');
$elapsed = 0;
$bar->display($elapsed);
while ($line = fgets($handle))
{
$elapsed++;
$bar->update($elapsed);
}
pclose($handle);
?>
PHP不是多线程的。exec((和company将阻止您的脚本,直到exec的任务完成。使用while((循环毫无意义,因为在svn调用完成之前,循环甚至不会开始运行。
如果你想运行多个并行svn,你需要使用popen(((或proc_open(((,它为你提供了一个文件句柄,你可以从中读取外部命令的输出,并同时拥有多个这样的外部命令。
使用输出缓冲
参见:
- http://php.net/manual/en/function.ob-start.php
- http://php.net/manual/en/function.ob-flush.php
- http://php.net/manual/en/function.ob-end-clean.php
并查看一些相关信息:
- PHP命令行上的输出缓冲
- Kohana 3命令行输出缓冲
AS我理解php同步执行脚本的问题。这意味着循环将只在exec命令之后运行,并且输出将不会更改,因为它已经完成了。尝试使用fork.