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

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

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

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

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

10 ответов

Решение

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

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

У меня есть серверная установка Windows Server 2003 под управлением IIS6 и PHP 5.2.3. На этом сервере у меня есть:

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

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

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

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

Есть еще несколько на обоих 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, он основан на той же схеме.

РЕДАКТИРОВАТЬ: После нескольких исследований, единственный способ выполнить команду 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

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

shell_exec('dir 2>&1');

Он отлично работает на Windows 8.1 64bit с включенным 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() вернуться в исходную папку, если вам нужно запустить другие программы.

Я знаю, что выбрал ответ, поскольку должен сделать это, но я все еще не понимаю, почему он не будет работать на моем компьютере, но я нашел запасное решение (с помощью 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.exe и выберите "Никогда не уведомлять"

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

Я использую: Apache Версия: 2.2.11
Версия PHP: 5.2.8
Windows 7 SP1 32bit

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

Вы можете проверить разрешения пользователя IIS для cmd.exe

cacls C:\WINDOWS\system32\cmd.exe

Если в выводе есть строка типа (ИМЯ КОМПЬЮТЕРА = имя вашего компьютера):

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

Это означает, что у пользователя нет прав на выполнение cmd.

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

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

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

Посмотрите на Apache error_log, может быть, вы найдете сообщение об ошибке.

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

$p = popen("dir", "r");
if (!$p)
    exit("Cannot run command.\n");
while (!feof($p))
    echo fgets($p);
pclose($p);
Другие вопросы по тегам