PHP reading shell_exec live output

Php

Php Problem Overview


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?

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

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.

Php Solutions


Solution 1 - Php

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.

But if you just want to dump it's result straight to the user you can cut to the chase and use 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.

Solution 2 - Php

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

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

<?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
}

Solution 3 - Php

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().

This was provided by the Symfony documentation:

> 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 = 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.

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPalumbo SoftwareView Question on Stackoverflow
Solution 1 - PhpHavenardView Answer on Stackoverflow
Solution 2 - PhpAmithView Answer on Stackoverflow
Solution 3 - PhpHPierceView Answer on Stackoverflow