php exec: не возвращает вывод

у меня есть эта проблема: На веб-сервере ISS установлен Windows 7 x64 professional, Zend server. Запуск этой команды в php:

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

$output is empty, $err=1. таким образом, exec не повторяет вывод, похоже, имеет некоторые ошибки. В PHP disable_functions пуст, php не находится в безопасном режиме, стандартен, я проверяю все опции. Кажется, это общие ошибки, даже поиск в google не дает результатов.

пожалуйста, напишите каждый его опыт и возможные решения или обходные пути.

10 ответов


есть несколько сообщений в соответствующих разделах руководства PHP, таких как этот:

у меня возникли проблемы с использованием команды PHP exec для выполнения любой партии файл. Выполнение других команд (т. е. "dir") работает нормально). Но если я выполнил пакетный файл, я не получил вывода из команды exec.

настройка сервера у меня состоит из Windows Server 2003 server работает IIS6 и PHP 5.2.3. На этом сервере, у меня есть:

  1. разрешения execute для пользователя Интернет c:\windows\system32\cmd.исполняемый.
  2. предоставлено всем - >полный контроль над каталогом, в котором записан пакетный файл.
  3. предоставлено всем - >полный контроль над всем c:\cygwin\bin каталог и его содержимое.
  4. предоставлено пользователю Интернета разрешение "войти в систему как пакет".
  5. указан полный путь к каждому файлу выполняется.
  6. проверили эти скрипты запуска из командной строки на сервере и они работают просто отлично.
  7. убедитесь, что %systemroot%\system32 находится в системном пути.

оказывается, что даже со всем вышеперечисленным на месте на сервере я пришлось указать полный путь к cmd.exe в вызове exec.

когда я использовал вызов: $output = exec("c:\windows\system32\cmd.exe /c $batchFileToRun");

потом все работало нормально. В моем положении, $batchFileToRun была фактический системный путь к пакету файл (т. е. результат вызова реального пути()).

есть еще несколько на обоих exec и shell_exec руководство страниц. Возможно, пройдя через них, вы встанете на ноги и будете работать на вас.


основная причина, по которой вы получаете нулевой вывод от команды, такой как dir - потому что dir не существует. Это часть командной строки, и решение этой конкретной проблемы хорошо, где-то на этой странице.

вы можете узнать это, нажав WIN + R, и введя в dir или start если на то пошло-появляется сообщение об ошибке!

как бы извращенно это ни звучало, я обнаружил, что самый надежный способ сделать что-либо, связанное с процессом в Windows с использованием объектной модели компонента. Вы сказали, чтобы мы поделились своим опытом, верно?

$меня слышит смех людей

уже обрел самообладание?

Итак, сначала мы создадим COM-объект:

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

тогда мы просто запустим то, что когда-либо должно быть запущено.

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

круто! Теперь это будет висеть, пока все не закончится в этом двоичном файле. Итак, что нам нужно сделать сейчас, это схватить выход.

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

есть некоторые другие вещи, которые вы можете сделать - выяснить, что идентификатор процесса, код ошибки и т. д. Хотя для Visual Basic, он основан на той же схеме.


EDIT: после нескольких исследований я вижу единственный способ выполнить команду DIR так:

cmd.exe /c dir c:\

таким образом, вы можете попробовать мое решение, используя:

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

вы также можете использовать функцию proc_open, которая дает вам доступ к stderr, коду состояния возврата и 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);

интересная часть с proc_open, по сравнению с другими функциями, такими как popen или exec, заключается в том, что она предоставляет параметры, специфичные для платформы windows, такие как:

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

Я знаю, что выбрал ответ, поскольку я должен это сделать, но я все еще не понимаю, почему он не будет работать на моем macchine, но я нашел резервное (используя proc_open) решение, используя другой метод:

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

надеюсь, это кому-то поможет.


У меня была такая же проблема и после того, как провел много часов и чашку кофе

просто отключение управления учетными записями пользователей (UAC) в Windows 7 короткий путь: P C:\Windows\System32\UserAccountControlSettings.исполняемый и выберите "никогда не уведомлять"

и php exec () отлично работает!

Я использую: Версия Apache: 2.2.11
PHP версия : 5.2.8
Windows 7 с пакетом обновления SP1 32бит

с наилучшими пожеланиями! : D


в целях безопасности ваш сервер может иметь ограниченный доступ к команде "exec". В этом случае, возможно, вам следует обратиться в хостинг-компанию, чтобы убрать это ограничение (если есть).


вы можете проверить permisssions пользователя IIS для УМК.exe

cacls C:\WINDOWS\system32\cmd.exe

Если на выходе есть строка like (COMPUTERNAME=имя Вашего компьютера):

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

Это означает, что пользователь не имеет прав на выполнение команды.

вы можете добавить разрешения на выполнение с помощью команды:

cacls C:\WINDOWS\system32\cmd.exe /E /G COMPUTERNAME\IUSR_COMPUTERNAME:R

попробуйте это:

shell_exec('dir 2>&1');

он отлично работает на Windows 8.1 64bits с включенным UAC.


Если вы находитесь на машине Windows и пытаетесь запустить исполняемый файл, как это:

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

и нет вывода или ваш файл не запущен, тогда вы должны попробовать следующее:

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

это должно сработать.

Это особенно удобно, если используется относительный путь от папки где скрипт находится, например:

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

и не забудьте chdir () вернуться в исходную папку, если вам нужно запустить другие программы.


взгляните на Apache error_log, возможно, вы найдете сообщение об ошибке.

кроме того, вы можете попробовать использовать popen вместо exec. Это дает больше контроля, потому что вы можете отделить запуск процесса от вывода read:

$p = popen("dir", "r");
if (!$p)
    exit("Cannot run command.\n");
while (!feof($p))
    echo fgets($p);
pclose($p);