PHP reading shell_exec live output
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.
More useful information
Note that if you are using sessions then having one of those running will prevent the user from loading other pages, as sessions enforce that concurrent requests cannot happen. To prevent this from being a problem, call session_write_close()
before the loop.
If your server is behind a nginx gateway, then the nginx buffering may be disruptive to the desired behavior. Set the header header('X-Accel-Buffering: no');
to hint nginx that it shouldn't do that. As headers are sent first, this has to be called in the beginning of the script, before any data is sent.
Bash script live output executed from PHP
Use the following:
<?php
ob_implicit_flush(true);
ob_end_flush();
$cmd = "bash /path/to/test.sh";
$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
);
$process = proc_open($cmd, $descriptorspec, $pipes, realpath('./'), array());
if (is_resource($process)) {
while ($s = fgets($pipes[1])) {
print $s;
}
}
?>
Change test.sh to:
#!/bin/bash
whoami
sleep 3
ls /
Explanation:
dmesg requires permissions. You need to grant webserver's user permissions for that. In my case apache2 is being run via www-data user.
ob_implicit_flush(true)
: Turns implicit flushing on. Implicit flushing will result in a flush operation after every output call, so that explicit calls to flush()
will no longer be needed.
ob_end_flush()
: Turns off output buffering, so we see results immediately.
Live output of shell script using `watch` in browser?
I would advise against the approach of using watch, and you are probably in for more trouble than you expect.
Firstly, long running command might be affected by the (default) PHP timeout, so you may have to tweak that.
Then, watch probably uses terminal sequences to clear the screen, and I am not sure how this translates to the output code.
I would rather suggest setting up a client side mechanism to periodically repeat a call to the sever side live.php.
This post on SO will help you get started.
jQuery, simple polling example
the page above uses the jquery library, but you could use native Javascript equivalent if you want to.
The simplest example (from that page) would be :
function poll(){
$("live.php", function(data){
$("#output_container").html(data);
});
}
setInterval(function(){ poll(); }, 5000);
In your page, set up a container for your results
<div id="output_container"></div>
In your example, you remove the watch from your script and replace it with the command you intended watching.
PHP Execute shell command asynchronously and retrieve live output
I've solved the problem by piping the output STDIN to a temporary file and then reading from it.
Here's my
Implementation
class ExecAsync {
public function __construct($cmd) {
$this->cmd = $cmd;
$this->cacheFile = ".cache-pipe-".uniqid();
$this->lineNumber = 0;
}
public function getLine() {
$file = new SplFileObject($this->cacheFile);
$file->seek($this->lineNumber);
if($file->valid())
{
$this->lineNumber++;
$current = $file->current();
return $current;
} else
return NULL;
}
public function hasFinished() {
if(file_exists(".status-".$this->cacheFile) ||
(!file_exists(".status-".$this->cacheFile) && !file_exists($this->cacheFile)))
{
unlink($this->cacheFile);
unlink(".status-".$this->cacheFile);
$this->lineNumber = 0;
return TRUE;
} else
return FALSE;
}
public function run() {
if($this->cmd) {
$out = exec('{ '.$this->cmd." > ".$this->cacheFile." && echo finished > .status-".$this->cacheFile.";} > /dev/null 2>/dev/null &");
}
}
}
Usage
$command = new ExecAsync("command to execute");
//run the command
$command->run();
/*We want to read from the command output as long as
*there are still lines left to read
*and the command hasn't finished yet
*if getLine returns NULL it means that we have caught up
*and there are no more lines left to read
*/
while(($line = $command->getLine()) || !$command->hasFinished())
{
if($line !== NULL)
{
echo $line."\n";
flush();
} else
{
usleep(10);
}
}
PHP reading shell_exec live output
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.
More useful information
Note that if you are using sessions then having one of those running will prevent the user from loading other pages, as sessions enforce that concurrent requests cannot happen. To prevent this from being a problem, call session_write_close()
before the loop.
If your server is behind a nginx gateway, then the nginx buffering may be disruptive to the desired behavior. Set the header header('X-Accel-Buffering: no');
to hint nginx that it shouldn't do that. As headers are sent first, this has to be called in the beginning of the script, before any data is sent.
Related Topics
Get Specific Columns Using "With()" Function in Laravel Eloquent
List of Big-O For PHP Functions
PHP Sort Array by Subarray Value
PHP: Convert Any String to Utf-8 Without Knowing the Original Character Set, or At Least Try
How to Add Http:// If It Doesn't Exist in the Url
What Are the Differences in Die() and Exit() in PHP
Nginx Location Configuration (Subfolders)
How to Automatically Start a Download in PHP
How to Make Pdf File Downloadable in HTML Link
How to Perform Static Code Analysis in PHP
How to Find Out If You'Re Using Https Without $_Server['Https']
Difference Between PHP Echo and PHP Return in Plain English
Upload Photo to Album With Facebook'S Graph API