7

I'm writing a PHP script that needs to make some calls to external command line utilities, which I am using exec() for. Had it all working fine locally, but when I moved it over to a live server it no longer works.

After some debugging I believe the problem is down to the STDERR redirection - if I attempt anything to do with redirection (redirect to STDIN, redirect to file) the command completely fails.

I wrote a simple bat script to simplify the test:

@echo off
echo STDOUT test
echo STDERR test 1>&2

And then in PHP:

<?php
$cmd = "_test.bat 2>&1";
exec($cmd, $output, $status);
var_dump($output);
var_dump($status);
?>

On the local server the result is $status=0, $output=array("STDOUT test", "STDERR test") as I would expect, but on the live server the result is $status=1, and output is an empty array!

If I remove the 1>&2 part, I get the same result for both (just the STDOUT part), so the command itself is clearly working as expected.

Is there something simple I am missing here? If it helps, the server is running Windows Server 2008 R2.

tom1990
  • 602
  • 8
  • 17
  • Maybe you should try using the absolute path of `_test.bat`. – l'L'l Aug 29 '14 at 09:06
  • Thanks for the comment, just tried using the full path and I still get the same result - The command works as long as I don't try to redirect STDERR with 2>&1. The problem is, I need to capture STDERR! – tom1990 Aug 29 '14 at 09:09
  • Check the `display_errors` directive in `php.ini` - it should be set as `stderr` – l'L'l Aug 29 '14 at 09:18
  • It was set to 1, I have changed it to stderr but it's still doing the same thing – tom1990 Aug 29 '14 at 09:25
  • Did you restart your webserver? If not I think you'll need to. I've only got one more suggestion. You can try using php's handler (http://stackoverflow.com/a/6447599/499581). – l'L'l Aug 29 '14 at 09:28
  • Had a go but still could not get exec to work properly! For now I have a workaround that uses proc_open() instead. I have added an answer below to show people what that is.. I still don't understand what the issue is with exec though! – tom1990 Aug 29 '14 at 11:17

2 Answers2

4

Update and answer for the benefit of anyone who comes here with a similar issue.

After spending a lot of time on this I gave up with exec() and gave proc_open() a try instead.

Doing it this way now works locally and on the server:

<?php
$cmd = "__test.bat";

$descriptorspec = array(
    0 => array("pipe", "r"), // STDIN
    1 => array("pipe", "w"), // STDOUT
    2 => array("pipe", "w"), // STDERR
);
$cwd = getcwd();
$env = null;

$proc = proc_open($cmd, $descriptorspec, $pipes, $cwd, $env);
if (is_resource($proc)) {
    // Output test:
    echo "STDOUT:<br />";
    echo "<pre>".stream_get_contents($pipes[1])."</pre>";
    echo "STDERR:<br />";
    echo "<pre>".stream_get_contents($pipes[2])."</pre>";
    $return_value = proc_close($proc);
    echo "Exited with status: {$return_value}";
}
?>

For some reason, missing out the getcwd() part causes the command to fail on the server unless I specify the complete path, whereas locally that is not an issue.

With this method I can append 2>&1 to redirect all output to STDIN. To output to a file, the manual shows that the $descriptorspec array can be modified e.g: 2 => array("file", "stderr.log", "a") (I have not yet tested this though)

One difference here is that if I want to retrieve the output in PHP, rather than getting all of the lines in an array, I need to read from the streams using stream_get_contents().

I still don't understand why there was an issue with using exec(), but this method seems to work both locally and on the server - If anyone knows why this could be, please let me know!

tom1990
  • 602
  • 8
  • 17
3

$o = null; $r = null; exec("php test.php 2>&1", $o, $r ); It's quite simple

gouchaoer
  • 535
  • 2
  • 7
  • 22