9

I have this problem: On a ISS web server, windows 7 x64 professional, zend server installed. Running this command under php:

exec('dir',$output, $err);

$output is empty, $err=1. So exec is not returing the output, it seems has some errors. Php disable_functions is empty, php is not on safe mode, is standard, I check all option. It seems to be a general errors, even search on google do not give results.

Please write every one his experince and eventual solutions or workarounds.

Nick Lockwood
  • 39,931
  • 11
  • 108
  • 100
albanx
  • 5,396
  • 8
  • 57
  • 89
  • maybe because there was an error? :) check what error 1 code means ... – Catalin Feb 02 '12 at 15:05
  • if i launch the same command on cmd shell with php script.php it works, when I launch it from browser(so web server) it does not work, i really do not understand where error is – albanx Feb 02 '12 at 15:10
  • @albanx: That would lead me to believe permissions if it works when you run it and not when IIS is. – Brad Christie Feb 02 '12 at 15:19
  • @albanx try appending `2>&1` to the command you are running, and see if you can see any error messages. – DaveRandom Feb 02 '12 at 15:27
  • even `2>&1` not giving result, also i think it may be permission problem but i can't figure it out, i give to iis user all possible permission – albanx Feb 02 '12 at 15:49

10 Answers10

14

There are a few posts to the relevant sections of the PHP Manual such as this one:

I was having trouble using the PHP exec command to execute any batch file. Executing other commands (i.e., "dir") works fine). But if I executed a batch file, I receieved no output from the exec command.

The server setup I have consists of Windows Server 2003 server running IIS6 and PHP 5.2.3. On this server, I have:

  1. Granted execute permissions to the Internet User on c:\windows\system32\cmd.exe.
  2. Granted Everyone->Full Control to the directory in which the batch file is written.
  3. Granted Everyone->Full Control on the entire c:\cygwin\bin directory and its contents.
  4. Granted the Internet User "log on as batch" permissions.
  5. Specified the full path to each file being executed.
  6. Tested these scripts running from the command line on the server and they work just fine.
  7. Ensured that %systemroot%\system32 is in the system path.

It turns out that even with all of the above in place on the server, I had to specify the full path to cmd.exe in the exec call.

When I used the call: $output = exec("c:\\windows\\system32\\cmd.exe /c $batchFileToRun");

then everything worked fine. In my situation, $batchFileToRun was the actual system path to the batch file (i.e., the result of a call to realpath()).

There are a few more on both the exec and shell_exec manual pages. Perhaps following through them will get it up and working for you.

Treffynnon
  • 20,415
  • 5
  • 59
  • 95
9

The main reason you get zero output from a command such as dir is because dir doesn't exist. It's part of the command prompt, and the solution to this particular problem is well, in this page somewhere.

You can find this out by pressing WIN + R, and typing in dir or start for that matter - an error message appears!

However perverse this may sound, I've found that the most reliable way to do anything process related in Windows is with using the Component Object Model. You said for us to share our experiences, right?

$me hears people laughing

Regained your composure yet?

So, first we'll create the COM object:

$pCOM = new COM("WScript.Shell");

Then, we'll just run what ever needs to be run.

$pShell = $pCom->Exec("C:\Random Folder\Whatever.exe");

Cool! Now, that'll hang until everything's finished in that binary. So, what we need to do now is grab the output.

$sStdOut = $pShell->StdOut->ReadAll;    # Standard output
$sStdErr = $pShell->StdErr->ReadAll;    # Error

There's some other things you can do - find out what was the process ID, error code, etc. Although this example is for Visual Basic, it's based on the same scheme.

Westie
  • 407
  • 3
  • 11
5

EDIT: After a few researchs, the only way i see to execute the DIR command is like that:

cmd.exe /c dir c:\

So you can try my solution using:

$command = 'cmd.exe /c dir c:\\';

You can use proc_open function too, which gives you access to stderr, return status code and stdout.

    $stdout = $stderr = $status = null;
    $descriptorspec = array(
       1 => array('pipe', 'w'),  // stdout is a pipe that the child will write to
       2 => array('pipe', 'w') // stderr is a pipe that the child will write to
    );

    $process = proc_open($command, $descriptorspec, $pipes);

    if (is_resource($process)) {
        // $pipes now looks like this:
        // 0 => writeable handle connected to child stdin
        // 1 => readable handle connected to child stdout
        // 2 => readable handle connected to child stderr

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

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

        // It is important that you close any pipes before calling
        // proc_close in order to avoid a deadlock
        $status = proc_close($process);
    }

    return array($status, $stdout, $stderr);

The interesting part with proc_open, compared to other functions like popen or exec, is that it provides options specific to windows platform like:

suppress_errors (windows only): suppresses errors generated by this function when it's set to TRUE
bypass_shell (windows only): bypass cmd.exe shell when set to TRUE
Florian Klein
  • 7,966
  • 1
  • 29
  • 39
2

Try this:

shell_exec('dir 2>&1');

It works fine on Windows 8.1 64bits with UAC enabled.

Pokechu22
  • 4,790
  • 8
  • 35
  • 59
anisite
  • 103
  • 2
  • 8
1

I know I have choosen an answer as I must do it but I still do not understand why it wont work on my macchine but I found a fallback (using proc_open) solution using another method:

public static function exec_alt($cmd)
{
    exec($cmd, $output);
    if (!$output) {
        /**
         * FIXME: for some reason exec() returns empty output array @mine,'s machine.
         *        Somehow proc_open() approach (below) works, but doesn't work at
         *        test machines - same empty output with both pipes and temporary
         *        files (not we bypass shell wrapper). So use it as a fallback.
         */
        $output = array();
        $handle = proc_open($cmd, array(1 => array('pipe', 'w')), $pipes, null, null, array('bypass_shell' => true));
        if (is_resource($handle)) {
            $output = explode("\n", stream_get_contents($pipes[1]));
            fclose($pipes[1]);
            proc_close($handle);
        }
    }
    return $output;
}

Hope this helps someone.

albanx
  • 5,396
  • 8
  • 57
  • 89
  • 1
    I think the key to this is the `bypass_shell` argument. From what I understand of it, `exec()` calls `cmd /c` on the command you pass it. And for whatever reason, it must fail to find this initial `cmd.exe` file (perhaps because of local environment variables or whatever). By bypassing the implicit shell provided by PHP, and explicitly telling the command to run "this particular `cmd` shell in this particular directory", PHP successfully does what you tell it to. – cartbeforehorse Apr 23 '13 at 06:21
1

I was the same problem and after spend a lot of hours and cup of coffee

Just Disabling User Account Control (UAC) in Windows 7 short path :P C:\Windows\System32\UserAccountControlSettings.exe and select "Never notify"

and php exec() works fine!

I use: Apache Version : 2.2.11
PHP Version : 5.2.8
Windows 7 SP1 32bit

Best regards! :D

  • I do not remember right now, but even me should had have disabled UAC at time, but even this should be keep in mind. Good one +1. – albanx Jun 16 '13 at 07:01
1

If you are on Windows machine and trying to run an executable like this:

shell_exec("C:\Programs\SomeDir\DirinDir\..\DirWithYourFile\YourFile.exe");

and there is no output or your file isn't started, then you should try this:

chdir("C:\Programs\SomeDir\DirinDir\..\DirWithYourFile");
shell_exec("YourFile.exe");

It should work.

This is particularly handy if one is using relative path from folder where script currently is being located, for example:

$dir = dirname(__FILE__).'\\..\\..\\..\\web\\';

And don't forget to chdir() back to originating folder if you need to run other programs.

developer
  • 91
  • 2
  • 4
0

For security purposes, your server may have restricted access to "exec" command. In this case, maybe you should contact the hosting company to remove this restriction (if there is one).

Peter O.
  • 28,965
  • 14
  • 72
  • 87
Giannis
  • 793
  • 5
  • 13
0

You can check permisssions of IIS User to cmd.exe

cacls C:\WINDOWS\system32\cmd.exe

If in output is line like (COMPUTERNAME=your computer name):

C:\WINDOWS\system32\cmd.exe COMPUTERNAME\IUSR_COMPUTERNAME:N

It means that user has no rights to execute cmd.

You can add execute permissions with command:

cacls C:\WINDOWS\system32\cmd.exe /E /G COMPUTERNAME\IUSR_COMPUTERNAME:R
li-on
  • 539
  • 2
  • 8
-1

Take a look at Apache error_log, maybe you'll find the error message.

Also, you can try using popen instead of exec. It gives more control because you can separate process start from output read:

$p = popen("dir", "r");
if (!$p)
    exit("Cannot run command.\n");
while (!feof($p))
    echo fgets($p);
pclose($p);
Milan Babuškov
  • 55,232
  • 47
  • 119
  • 176