Reputation: 989
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.
Upvotes: 77
Views: 36320
Reputation: 401002
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
"
Upvotes: 93
Reputation: 2193
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.
Upvotes: 6
Reputation: 1
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");
}
Upvotes: 0
Reputation: 8288
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)
Upvotes: 12
Reputation: 282875
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.
Upvotes: 64
Reputation: 1082
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);
Upvotes: 7