77

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.

2
  • I will recommend you to use Symfony/Process component. Commented Feb 6, 2018 at 18:03
  • @martin Is there an answer which helped you? Please mark it as accepted. Commented Apr 9, 2020 at 10:33

6 Answers 6

93

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 = [
   0 => ["pipe", "r"],  // stdin
   1 => ["pipe", "w"],  // stdout
   2 => ["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
"
Sign up to request clarification or add additional context in comments.

5 Comments

When using this, make sure you all use proc_close when you're all done for cleanliness' sake.
Why you redirect result in a process var?
@BriceFavre He puts it in a process var so that he can close it later with proc_close, which will also return the return code the process exited with. $returnCode = proc_close( $process );
in Windows evironments, proc_open seems to be unreliable.
How to merge both pipes together, in the order the data comes in?
65

A little function that might be helpful:

function my_shell_exec($cmd, &$stdout=null, &$stderr=null) {
    $proc = proc_open($cmd,[
        1 => ['pipe','w'],
        2 => ['pipe','w'],
    ],$pipes);
    $stdout = stream_get_contents($pipes[1]);
    fclose($pipes[1]);
    $stderr = stream_get_contents($pipes[2]);
    fclose($pipes[2]);
    return proc_close($proc);
}

The exit code is returned and STDOUT and STDERR are reference params if you need them.

1 Comment

Kind of surprising they haven't implemented something like this into stock PHP yet.
12

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)

Comments

7

Another way to get unmerged stdout/stderr.

$pp_name = "/tmp/pp_test";
@unlink($pp_name);
posix_mkfifo($pp_name, 0777);
$pp = fopen($pp_name, "r+");
stream_set_blocking($pp, FALSE);
exec("wget -O - http://www.youtube.com 2>$pp_name", $r_stdout);
$r_stderr = stream_get_contents($pp);
var_dump($r_stderr);
fclose($pp);
unlink($pp_name);

If you want to ignore stdout and get only stderr, you can try this:

exec("wget -O - http://www.youtube.com 2>&1 >/dev/null", $r_stderr);

Comments

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

Comments

0

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");
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.