PHP读取shell_exec实时输出

Pal*_*are 45 php

我只是shell_exec在我的Linux服务器上试验PHP .这是一个非常酷的功能,到目前为止我真的很享受它.有没有办法查看命令运行时正在进行的实时输出?

例如,如果ping stackoverflow.com运行时,在ping目标地址时,每次ping时,都会用PHP显示结果?那可能吗?

我很乐意看到缓冲区正在运行时的实时更新.也许这不可能,但肯定会很好.

这是我正在尝试的代码,我尝试过的每一种方法总是在命令完成后显示结果.

<?php

  $cmd = 'ping -c 10 127.0.0.1';

  $output = shell_exec($cmd);

  echo "<pre>$output</pre>";

?>
Run Code Online (Sandbox Code Playgroud)

我试过把这个echo部分放在一个循环中,但仍然没有运气.任何人都有任何建议让它显示屏幕上的实时输出而不是等到命令完成?

我试过exec,shell_exec,system,和passthru.它们中的每个人都在完成后显示内容.除非我使用了错误的语法或者我没有正确设置循环.

Hav*_*ard 88

要读取进程的输出,popen()就是要走的路.您的脚本将与程序并行运行,您可以通过读取和写入输出/输入来与它进行交互,就好像它是一个文件一样.

但是,如果您只是想将结果直接转储给用户,您可以切入追逐并使用passthru():

echo '<pre>';
passthru($cmd);
echo '</pre>';
Run Code Online (Sandbox Code Playgroud)

如果要在程序运行时在运行时显示输出,可以执行以下操作:

while (@ ob_end_flush()); // end all output buffers if any

$proc = popen($cmd, 'r');
echo '<pre>';
while (!feof($proc))
{
    echo fread($proc, 4096);
    @ flush();
}
echo '</pre>';
Run Code Online (Sandbox Code Playgroud)

此代码应运行该命令并在运行时将输出直接推送给最终用户.

  • 是的,诀窍是强制执行任何缓冲都不会发生.这段代码确保了这一点.通常PHP只会在累积至少4KB的数据时刷新输出. (2认同)
  • 从python进程捕获输出时,您需要禁用输出缓冲:python -u / path / to / watch ...或PYTHONUNBUFFERED = 1 watch ... (2认同)
  • 如果您使用 Nginx,还应该添加 `header('X-Accel-Buffering: no');` 以确保 gzip 和 fastcgi_buffering 针对单个响应关闭。我想知道为什么这段代码不起作用,这就是原因。 (2认同)

Ami*_*ith 21

首先,感谢Havenard的片段 - 它帮助了很多!

我发现有用的Havenard代码的略微修改版本.

<?php
/**
 * Execute the given command by displaying console output live to the user.
 *  @param  string  cmd          :  command to be executed
 *  @return array   exit_status  :  exit status of the executed command
 *                  output       :  console output of the executed command
 */
function liveExecuteCommand($cmd)
{

    while (@ ob_end_flush()); // end all output buffers if any

    $proc = popen("$cmd 2>&1 ; echo Exit status : $?", 'r');

    $live_output     = "";
    $complete_output = "";

    while (!feof($proc))
    {
        $live_output     = fread($proc, 4096);
        $complete_output = $complete_output . $live_output;
        echo "$live_output";
        @ flush();
    }

    pclose($proc);

    // get exit status
    preg_match('/[0-9]+$/', $complete_output, $matches);

    // return exit status and intended output
    return array (
                    'exit_status'  => intval($matches[0]),
                    'output'       => str_replace("Exit status : " . $matches[0], '', $complete_output)
                 );
}
?>
Run Code Online (Sandbox Code Playgroud)

样品用法:

$result = liveExecuteCommand('ls -la');

if($result['exit_status'] === 0){
   // do something if command execution succeeds
} else {
    // do something on failure
}
Run Code Online (Sandbox Code Playgroud)


HPi*_*rce 6

如果您愿意下载依赖项,Symfony 的处理器组件会执行此操作。我发现使用这个更干净的界面而不是用popen()或重新发明任何东西passthru()

这是由 Symfony 文档提供的:

您还可以使用带有 foreach 构造的 Process 类在生成时获取输出。默认情况下,循环在进入下一次迭代之前等待新的输出:

$process = new Process('ls -lsa');
$process->start();

foreach ($process as $type => $data) {
    if ($process::OUT === $type) {
        echo "\nRead from stdout: ".$data;
    } else { // $process::ERR === $type
        echo "\nRead from stderr: ".$data;
    }
}
Run Code Online (Sandbox Code Playgroud)

作为警告,我遇到了一些问题,PHP 和 Nginx 在将输出发送到浏览器之前尝试缓冲输出。您可以通过在 php.ini: 中将其关闭来禁用 PHP 中的输出缓冲output_buffering = off。显然有一种方法可以在 Nginx 中禁用它,但我最终使用 PHP 内置服务器进行测试以避免麻烦。

我在 Gitlab 上提供了一个完整的例子:https ://gitlab.com/hpierce1102/web-shell-output-streaming