PHP读取shell_exec实时输出

时间:2021-04-03 17:33:18

I'm just experimenting with PHP and shell_exec on my Linux server. It's a really cool function to use and I am really enjoying it so far. Is there a way to view the live output that is going on while the command is running?

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

For example, if ping *.com was run, while it is pinging the target address, every time it pings, show the results with PHP? Is that possible?

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

I would love to see the live update of the buffer as it's running. Maybe it's not possible but it sure would be nice.

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

This is the code I am trying and every way I have tried it always displays the results after the command is finished.

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

<?php

  $cmd = 'ping -c 10 127.0.0.1';

  $output = shell_exec($cmd);

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

?>

I've tried putting the echo part in a loop but still no luck. Anyone have any suggestions on making it show the live output to the screen instead of waiting until the command is complete?

我已经尝试将回声部分放在循环中,但仍然没有运气。任何人都有任何建议让它显示实时输出到屏幕而不是等到命令完成?

I've tried exec, shell_exec, system, and passthru. Everyone of them displays the content after it's finished. Unless I'm using the wrong syntax or I'm not setting up the loop correctly.

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

3 个解决方案

#1


67  

To read the output of a process, popen() is the way to go. Your script will run in parallel with the program and you can interact with it by reading and writing it's output/input as if it was a file.

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

But if you just want to dump it's result straight to the user you can cut to the chase and use passthru():

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

echo '<pre>';
passthru($cmd);
echo '</pre>';

If you want to display the output at run time as the program goes, you can do this:

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

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>';

This code should run the command and push the output straight to the end user at run time.

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

#2


17  

First of all, thanks Havenard for your snippet - it helped a lot!

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

A slightly modified version of Havenard's code which i found useful.

我发现有用的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)
                 );
}
?>

Sample Usage :

样品用法:

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

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

#3


2  

If you're willing to download a dependency, Symfony's processor component does this. I found the interface to working with this cleaner than reinventing anything myself with popen() or passthru().

如果您愿意下载依赖项,Symfony的处理器组件会执行此操作。我找到了使用这个清洁工具的界面,而不是用popen()或passthru()重新创造任何东西。

This was provided by the Symfony documentation:

这是由Symfony文档提供的:

You can also use the Process class with the foreach construct to get the output while it is generated. By default, the loop waits for new output before going to the next iteration:

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

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

As a warning, I've run into some problems PHP and Nginx trying to buffer the output before sending it to the browser. You can disable output buffering in PHP by turning it off in php.ini: output_buffering = off. There's apparently a way to disable it in Nginx, but I ended up using the PHP built in server for my testing to avoid the hassle.

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

I put up a full example of this on Gitlab: https://gitlab.com/hpierce1102/web-shell-output-streaming

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

#1


67  

To read the output of a process, popen() is the way to go. Your script will run in parallel with the program and you can interact with it by reading and writing it's output/input as if it was a file.

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

But if you just want to dump it's result straight to the user you can cut to the chase and use passthru():

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

echo '<pre>';
passthru($cmd);
echo '</pre>';

If you want to display the output at run time as the program goes, you can do this:

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

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>';

This code should run the command and push the output straight to the end user at run time.

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

#2


17  

First of all, thanks Havenard for your snippet - it helped a lot!

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

A slightly modified version of Havenard's code which i found useful.

我发现有用的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)
                 );
}
?>

Sample Usage :

样品用法:

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

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

#3


2  

If you're willing to download a dependency, Symfony's processor component does this. I found the interface to working with this cleaner than reinventing anything myself with popen() or passthru().

如果您愿意下载依赖项,Symfony的处理器组件会执行此操作。我找到了使用这个清洁工具的界面,而不是用popen()或passthru()重新创造任何东西。

This was provided by the Symfony documentation:

这是由Symfony文档提供的:

You can also use the Process class with the foreach construct to get the output while it is generated. By default, the loop waits for new output before going to the next iteration:

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

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

As a warning, I've run into some problems PHP and Nginx trying to buffer the output before sending it to the browser. You can disable output buffering in PHP by turning it off in php.ini: output_buffering = off. There's apparently a way to disable it in Nginx, but I ended up using the PHP built in server for my testing to avoid the hassle.

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

I put up a full example of this on Gitlab: https://gitlab.com/hpierce1102/web-shell-output-streaming

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