相关文章推荐
活泼的铁链  ·  Java ...·  1 年前    · 
憨厚的可乐  ·  javascript - React ...·  1 年前    · 

在PHP中运行具有实时输出的进程

77 人关注

我正试图在一个网页上运行一个进程,并实时返回其输出。例如,如果我运行'ping'进程,它应该在每次返回新行时更新我的页面(现在,当我使用exec(command, output)时,我被迫使用-c选项,并等待进程结束后在我的网页上看到输出)。有可能在php中这样做吗?

我还想知道,当有人离开页面时,杀死这种进程的正确方法是什么。在 "ping "进程的情况下,我仍然能够在系统监视器中看到该进程在运行(这有什么意义)。

2 个评论
Nginx的X-Accel缓冲可以阻止接受的答案,为了使其工作,在之前添加这个头: ``header('X-Accel-Buffering: no'); ``
对我来说,Nginx的缓冲是个问题,上述评论解决了我的问题。
php
linux
apache
process
real-time
Maksim Vi.
Maksim Vi.
发布于 2009-08-15
12 个回答
egafni
egafni
发布于 2022-04-14
已采纳
0 人赞同

This worked for me:

$cmd = "ping 127.0.0.1";
$descriptorspec = array(
   0 => array("pipe", "r"),   // stdin is a pipe that the child will read from
   1 => array("pipe", "w"),   // stdout is a pipe that the child will write to
   2 => array("pipe", "w")    // stderr is a pipe that the child will write to
flush();
$process = proc_open($cmd, $descriptorspec, $pipes, realpath('./'), array());
echo "<pre>";
if (is_resource($process)) {
    while ($s = fgets($pipes[1])) {
        print $s;
        flush();
echo "</pre>";
    
在Fedora php 5+上,这对我来说没有任何作用。
+1对我来说是有效的,但只有当我在开头加上 ob_implicit_flush(true);ob_end_flush(); 时才有效。
将这一点与罗伯的答案结合起来,你就有了一个目标。
我把它包装在一个函数中,所以我可以像 shell_exec 一样调用它。如果你只想显示输出,而不对其进行任何处理,那就很好用。
好的例子是在 proc_open() PHP手册页
Robb
Robb
发布于 2022-04-14
0 人赞同

这是一个显示你的shell命令的实时输出的好方法。

header("Content-type: text/plain"); // tell php to automatically flush after every output // including lines of output produced by shell commands disable_ob(); $command = 'rsync -avz /your/directory1 /your/directory2'; system($command);

You will need this function to prevent output buffering:

function disable_ob() {
    // Turn off output buffering
    ini_set('output_buffering', 'off');
    // Turn off PHP output compression
    ini_set('zlib.output_compression', false);
    // Implicitly flush the buffer(s)
    ini_set('implicit_flush', true);
    ob_implicit_flush(true);
    // Clear, and turn off output buffering
    while (ob_get_level() > 0) {
        // Get the curent level
        $level = ob_get_level();
        // End the buffering
        ob_end_clean();
        // If the current level has not changed, abort
        if (ob_get_level() == $level) break;
    // Disable apache output buffering/compression
    if (function_exists('apache_setenv')) {
        apache_setenv('no-gzip', '1');
        apache_setenv('dont-vary', '1');

但它并不是在我试过的每台服务器上都能工作,我希望我能提供建议,在你的php配置中寻找什么,以确定你是否应该把你的头发拉出来试图让这种类型的行为在你的服务器上工作! 还有人知道吗?

这里有一个纯PHP的假例子。

header("Content-type: text/plain"); disable_ob(); for($i=0;$i<10;$i++) echo $i . "\n"; usleep(300000);

我希望这能帮助其他通过谷歌搜索到这里的人。

This doesn't work with exec() . system() works fine though.
这对我来说非常好,它不是不断地更新,而是每隔一段时间就更新一次。它比什么都好。谢谢你。
在CentOS 6.5 / apache 2.2 / php 5.3上,这产生了我想要的效果。
这对我来说很好用(OS X 10.12,Apache 2.2.3,PHP 7.0.9),但由于某些原因,只有在X-Content-Type-Options头也被设置的情况下。【替换代码0
由于浏览器通常会等待一些初始数据包,你可以在开始时发送大量的空白,这样初始缓冲区就被填满了。 for ($i=0; $i < 100000; $i++) { echo " "; } echo "\n"; flush();
Pixsa
Pixsa
发布于 2022-04-14
0 人赞同

检查了所有的答案,都没有用......。

Found solution Here

它在WINDOWS上是有效的(我认为这个答案对在那里搜索的用户有帮助)。

$a = popen('ping www.google.com', 'r'); while($b = fgets($a, 2048)) { echo $b."<br>\n"; ob_flush();flush(); pclose($a);
当我不得不运行一个Python脚本时,这就是对我有用的一个。其他的不太行得通。
romaninsh
romaninsh
发布于 2022-04-14
0 人赞同

这里描述了一个使用现代HTML5服务器端事件来解决这个老问题的更好方案。

http://www.w3schools.com/html/html5_serversentevents.asp

http://sink.agiletoolkit.org/realtime/console

Code: https://github.com/atk4/sink/blob/master/admin/page/realtime/console.php#L40

(作为Agile Toolkit框架中的一个模块实施)

ed22
IE/Edge does not support this... ;)
mpen
mpen
发布于 2022-04-14
0 人赞同

对于命令行的使用。

function execute($cmd) {
    $proc = proc_open($cmd, [['pipe','r'],['pipe','w'],['pipe','w']], $pipes);
    while(($line = fgets($pipes[1])) !== false) {
        fwrite(STDOUT,$line);
    while(($line = fgets($pipes[2])) !== false) {
        fwrite(STDERR,$line);
    fclose($pipes[0]);
    fclose($pipes[1]);
    fclose($pipes[2]);
    return proc_close($proc);

如果你试图运行一个文件,你可能需要先给它执行权限。

chmod('/path/to/script',0755);
    
raminious
raminious
发布于 2022-04-14
0 人赞同

试试这个(在Windows机器+wamp服务器上测试)。

        header('Content-Encoding: none;');
        set_time_limit(0);
        $handle = popen("<<< Your Shell Command >>>", "r");
        if (ob_get_level() == 0) 
            ob_start();
        while(!feof($handle)) {
            $buffer = fgets($handle);
            $buffer = trim(htmlspecialchars($buffer));
            echo $buffer . "<br />";
            echo str_pad('', 4096);    
            ob_flush();
            flush();
            sleep(1);
        pclose($handle);
        ob_end_flush();
    
这个答案是错误的。问题是如何获得实时的页面输出。使用ob_start(),你的做法正好相反。你把所有的输出写到服务器内部的缓冲区,而不是立即发送给浏览器
我已经试过你的代码,,实际上它不是实时显示shell命令,而只是等待shell命令完成加载所有文本并使用sleep(1)一点一点地显示它。
Nux
Nux
发布于 2022-04-14
0 人赞同

我试过Windows上的各种PHP执行命令,发现它们差别很大。

  • Don't work for streaming: shell_exec , exec , passthru
  • Kind of works: proc_open , popen -- "kind of" because you cannot pass arguments to your command (i.e. wont' work with my.exe --something , will work with _my_something.bat ).
  • 最好的(最简单的)方法是。

  • You must make sure your exe is flushing commands (see printf flushing problem ). Without this you will most likely receive batches of about 4096 bytes of text whatever you do.
  • If you can, use header('Content-Type: text/event-stream'); (instead of header('Content-Type: text/plain; charset=...'); ). This will not work in all browsers/clients though! Streaming will work without this, but at least first lines will be buffered by the browser.
  • You also might want to disable cache header('Cache-Control: no-cache'); .
  • Turn off output buffering (either in php.ini or with ini_set('output_buffering', 'off'); ). This might also have to be done in Apache/Nginx/whatever server you use in front.
  • Turn of compression (either in php.ini or with ini_set('zlib.output_compression', false); ). This might also have to be done in Apache/Nginx/whatever server you use in front.
  • 因此,在你的C++程序中,你要做这样的事情(同样,其他解决方案见 printf flushing problem ):

    Logger::log(...) {
      printf (text);
      fflush(stdout);
    

    In PHP you do something like:

    function setupStreaming() {
        // Turn off output buffering
        ini_set('output_buffering', 'off');
        // Turn off PHP output compression
        ini_set('zlib.output_compression', false);
        // Disable Apache output buffering/compression
        if (function_exists('apache_setenv')) {
            apache_setenv('no-gzip', '1');
            apache_setenv('dont-vary', '1');
    function runStreamingCommand($cmd){
        echo "\nrunning $cmd\n";
        system($cmd);
    setupStreaming();
    runStreamingCommand($cmd);
        
    Todd Owen
    Todd Owen
    发布于 2022-04-14
    0 人赞同

    First check whether flush() 为你工作。如果是这样,很好,如果不是这样,可能意味着网络服务器由于某种原因在缓冲,例如mod_gzip被启用。

    对于像 ping 这样的东西,最简单的技术是在 PHP 中循环,多次运行 "ping -c 1",并在每次输出后调用 flush()。假设 PHP 被配置为在用户关闭 HTTP 连接时终止(这通常是默认的,或者可以调用 ignore_user_abort(false) 来确定),那么也不需要担心 ping 进程失控的问题。

    如果真的有必要,你只运行子进程 once 并连续显示其输出,这可能比较困难 -- 你可能需要在后台运行它,将输出重定向到一个流,然后让 PHP 将这个流回传给用户,中间穿插一些常规的 flush()调用。

    如果你想通过PHP来运行系统命令,可以参考一下。 执行委员会的文件 .

    但我不建议在一个高流量的网站上这样做,为每个请求分叉一个进程是一个相当大的过程。一些程序提供了将其进程ID写入文件的选项,这样你就可以检查并随意终止该进程,但对于像PING这样的命令,我不确定这是否可行,请查看手册页面。

    你最好在远程主机上的预期监听端口(IE:HTTP的80端口)上打开一个套接字,这样你就知道在用户区和网络上的一切都在顺利进行。

    如果你试图输出二进制数据,请研究一下 php的页眉功能 并确保你设置了适当的 content-type ,以及 content-disposition . Review 文件 详见 "使用/禁用输出缓冲区 "一节,以了解更多信息。

    Yatin
    发布于 2022-04-14
    0 人赞同

    试着改变php.ini文件中的设置 "output_buffering = Off"。你应该可以在页面上得到实时的输出。 使用系统命令而不是exec......系统命令将刷新输出。

    greenone83
    greenone83
    发布于 2022-04-14
    0 人赞同

    为什么不干脆把输出的内容输入一个日志文件,然后用这个文件把内容返回给客户。

    Luís Filipe Costa Carvalho
    Luís Filipe Costa Carvalho
    发布于 2022-04-14
    0 人赞同

    我有同样的问题,只是可以用Symfony Process Components ( https://symfony.com/doc/current/components/process.html )

    快速的例子。

    use Symfony\Component\Process\Process; $process = new Process(['ls', '-lsa']); $process->run(function ($type, $buffer) { if (Process::ERR === $type) { echo 'ERR > '.$buffer; } else { echo 'OUT > '.$buffer;