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. На этом сервере у меня есть:
- Предоставлено разрешение на выполнение для пользователя Интернета на c:\windows\system32\cmd.exe.
- Предоставлено Everyone-> Полный доступ к каталогу, в который записан командный файл.
- Предоставлено Everyone-> Полный доступ ко всему каталогу c:\cygwin\bin и его содержимому.
- Предоставил пользователю Интернета права "входить как пакет".
- Указан полный путь к каждому исполняемому файлу.
- Протестировал эти скрипты, запущенные из командной строки на сервере, и они работают просто отлично.
- Убедитесь, что%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);