PHP StdErr after Exec()

Viewed 31477

In PHP I am executing a command with exec(), and it returns if successful an URL;

$url = exec('report');

However, I want to check stderr, if something went wrong. How would I read the stream? I want to use php://stderr, but I am not sure how to use it.

6 Answers

If you want to execute a command, and get both stderr and stdout, not "merged", a solution would probably to use proc_open, which provides a great level of control over the command that's being executed -- including a way to pipe stdin/stdout/stderr.

And here is an example : let's consider we have this shell-script, in test.sh, which writes to both stderr and stdout :

#!/bin/bash

echo 'this is on stdout';
echo 'this is on stdout too';

echo 'this is on stderr' >&2;
echo 'this is on stderr too' >&2;

Now, let's code some PHP, in temp.php -- first, we initialize the i/o descriptors :

$descriptorspec = array(
   0 => array("pipe", "r"),  // stdin
   1 => array("pipe", "w"),  // stdout
   2 => array("pipe", "w"),  // stderr
);

And, then, execute the test.sh command, using those descriptors, in the current directory, and saying the i/o should be from/to $pipes :

$process = proc_open('./test.sh', $descriptorspec, $pipes, dirname(__FILE__), null);

We can now read from the two output pipes :

$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);

$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);

And, if we output the content of those two variables :

echo "stdout : \n";
var_dump($stdout);

echo "stderr :\n";
var_dump($stderr);

We get the following output when executing the temp.php script :

$ php ./temp.php
stdout :
string(40) "this is on stdout
this is on stdout too
"
stderr :
string(40) "this is on stderr
this is on stderr too
"

The short way to do such a things with exec is to return the exit code ( status of the command )

Note that I am trying to list a non-exists directory /non-dir/

exec('ls /non-dir/', $out, $retval);
var_dump($retval);

Output

ls: cannot access '/non-dir/': No such file or directory
int(2)

Normally in unix-based system most of successful statuses codes is ( 0 ) so you can check your $retval to know the status of the command.

to dismiss the error from listing an invalid path ls: cannot access '/non-dir/': No such file or directory you can redirect your stderr to null

exec('ls /non-dir/ 2>/dev/null', $out, $retval);
var_dump($retval);

this will output :

int(2)

also if you need the error string to use it in any scenario you may redirect your stderr to the stdout.

exec('ls /non-dir/ 2>&1', $out, $retval);
print_r($out);
var_dump($retval);

this will output the following:

Array
(
    [0] => ls: cannot access '/non-dir/': No such file or directory
)
int(2)
exec("{$command} 2>&1"
    ,$output
    ,$exitCode
    );
  • 2>&1 redirects stderr to stdout for consistent success / fail behaviour.

  • $exitCode determines $command completion status.

  • $output contains all output associated with $exitCode.

Slightly ugly but good enough. Put the stderr into a temp file and read it back.

$tmp = tempnam("/tmp", "ERR_");
exec('report 2> ' . escapeshellarg($tmp), $stdout, $retcode);
$stderr = file_get_contents($tmp);
unlink($tmp);
if ($retcode == 0)
{
    // good
    $url = $stdout[0];
} else {
    // bad
    error_log("FAIL: $stderr");
}
Related