(PHP) Live output proc_open

1.2k views Asked by At

i have tried many time by using flush() to make the script work synchronously, the script prints only data of the first command "gcloud compute ssh yellow" and "ls -la", I am looking to make the script prints the output on every executed fputs().

<?php

$descr = array( 0 => array('pipe','r',),1 => array('pipe','w',),2 => array('pipe','w',),);
$pipes = array();
$process = proc_open("gcloud compute ssh yellow", $descr, $pipes);

if (is_resource($process)) {
    sleep(2);
    $commands = ["ls -la", "cd /home", "ls", "sudo ifconfig", "ls -l"];     
    foreach ($commands as $command) {    
        fputs($pipes[0], $command . " \n");
        while ($f = fgets($pipes[1])) {
            echo $f;
        }
    }
    fclose($pipes[0]);  
    fclose($pipes[1]);
    while ($f = fgets($pipes[2])) {
        echo "\n\n## ==>> ";
        echo $f;
    }
    fclose($pipes[2]);
    proc_close($process);

}

Thanks in advance

2

There are 2 answers

1
miken32 On

I believe the problem is the loop you have waiting for input. fgets will only return false if it encounters EOF. Otherwise it returns the line that it read; because the linefeed is included, it doesn't return anything that can be typecast to false. You can use stream_get_line() instead, which does not return the EOL character. Note this would still require your command to return an empty line after its output so it can evaluate to false and break the while loop.

<?php
$prog     = "gcloud compute ssh yellow";
$commands = ["ls -la", "cd /home", "ls", "sudo ifconfig", "ls -l"];
$descr    = [0 => ['pipe','r'], 1 => ['pipe','w'], 2 =>['pipe','w']];
$pipes    = [];
$process  = proc_open($prog, $descr, $pipes);

if (is_resource($process)) {
    sleep(2);
    foreach ($commands as $command) {
        fputs($pipes[0], $command . PHP_EOL);
        while ($f = stream_get_line($pipes[1], 256)) {
            echo $f . PHP_EOL;
        }
    }
    fclose($pipes[0]);
    fclose($pipes[1]);
    fclose($pipes[2]);
    proc_close($process);
}

Another option would be to gather the output outside the loop, although this would require you to parse the output if you need to know what output came from what command.

<?php
$prog     = "gcloud compute ssh yellow";
$commands = ["ls -la", "cd /home", "ls", "sudo ifconfig", "ls -l"];
$descr    = [0 => ['pipe','r'], 1 => ['pipe','w'], 2 =>['pipe','w']];
$pipes    = [];
$process  = proc_open($prog, $descr, $pipes);

if (is_resource($process)) {
    sleep(2);
    foreach ($commands as $command) {
        fputs($pipes[0], $command . PHP_EOL);
    }
    fclose($pipes[0]);
    $return = stream_get_contents($pipes[1]);
    $errors = stream_get_contents($pipes[2]);
    fclose($pipes[1]);
    fclose($pipes[2]);
    proc_close($process);
}
2
Jerl92 On

I was able to shell_exec with a out.log, with my Wordpress plugin, in admin and to show the last line in other AJAX function.

You can go see my GitHub repo to see what it looks like, on line 516 of function my_url() on this file, and my_dl() will update the result div.

This is a way to do a long shell_exec command and this will update the output area.

The thing is to do shell_exec() with 2>&1 & at the end to save the output in text file.

    $cmd = "youtube-dl -o '$path_implode%(id)s.%(ext)s' -f best --extract-audio --audio-format mp3 --prefer-avconv --write-info-json -k " . $url;
    $res = shell_exec(''.$cmd.' > '.$path_implode.'out.log 2>&1 &');

And with a AJAX function in loop with setTimeout() get the file contents and split line by line.

    $html = file_get_contents($path_implode.'out.log');
    $res[0] = preg_split("/\r\n|\n|\r/", $html);