php exec:不返回输出
Posted
技术标签:
【中文标题】php exec:不返回输出【英文标题】:php exec: does not return output 【发布时间】:2012-02-02 15:03:21 【问题描述】:我有这个问题: 在 ISS Web 服务器上,安装了 windows 7 x64 专业版和 zend 服务器。在php下运行这个命令:
exec('dir',$output, $err);
$output is empty, $err=1.
所以 exec 没有返回输出,似乎有一些错误。
php disable_functions
为空,php 未处于安全模式,是标准的,我检查所有选项。
这似乎是一个普遍的错误,即使在谷歌上搜索也不给出结果。
请写下每个人的经验以及最终的解决方案或变通办法。
【问题讨论】:
可能是因为出现了错误? :) 检查错误 1 代码的含义... 如果我在 cmd shell 上使用 php script.php 启动相同的命令,它可以工作,当我从浏览器(所以 Web 服务器)启动它时它不起作用,我真的不明白错误在哪里 @albanx:这会让我相信权限,如果它在你运行它而不是在 IIS 运行时有效。 @albanx 尝试将2>&1
附加到您正在运行的命令中,看看是否可以看到任何错误消息。
即使2>&1
没有给出结果,我也认为可能是权限问题但我无法弄清楚,我给了iis用户所有可能的权限
【参考方案1】:
PHP 手册的相关部分有一些帖子,例如this one:
我在使用 PHP exec 命令执行任何批处理时遇到问题 文件。执行其他命令(即“dir”)工作正常)。但如果我 执行了一个批处理文件,我没有收到 exec 命令的输出。
我的服务器设置包括运行 Windows Server 2003 服务器 IIS6 和 PHP 5.2.3。在这台服务器上,我有:
授予 Internet 用户对 c:\windows\system32\cmd.exe 的执行权限。 授予所有人->对写入批处理文件的目录的完全控制权。 授予所有人->对整个 c:\cygwin\bin 目录及其内容的完全控制权。 授予 Internet 用户“批量登录”权限。 指定每个正在执行的文件的完整路径。 测试了这些从服务器上的命令行运行的脚本,它们运行良好。 确保 %systemroot%\system32 在系统路径中。
事实证明,即使在服务器上安装了上述所有内容,我 必须在 exec 调用中指定 cmd.exe 的完整路径。
当我使用电话时:
$output = exec("c:\\windows\\system32\\cmd.exe
/c $batchFileToRun");
然后一切正常。在我的情况下,
$batchFileToRun
是 批处理文件的实际系统路径(即调用 真实路径())。
exec
和 shell_exec
手册页上还有更多内容。也许跟随他们会得到它并为你工作。
【讨论】:
我会尝试按照这个步骤,但这不是批量执行。【参考方案2】:您从诸如dir
之类的命令获得零输出的主要原因是因为dir
不存在。它是命令提示符的一部分,这个特定问题的解决方案很好,在这个页面的某个地方。
您可以通过按WIN + R
并输入dir
或start
来查找此问题 - 出现错误消息!
无论这听起来多么有悖常理,我发现在 Windows 中执行任何相关进程的最可靠方法是使用组件对象模型。你说让我们分享我们的经验,对吧?
$me 听到人们在笑
镇定下来了吗?
所以,首先我们将创建 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
您还可以做一些其他事情 - 找出进程 ID、错误代码等。虽然 this example 用于 Visual Basic,但它基于相同的方案。
【讨论】:
【参考方案3】:编辑:经过一些研究,我看到执行 DIR 命令的唯一方法是这样的:
cmd.exe /c dir c:\
所以你可以试试我的解决方案:
$command = 'cmd.exe /c dir c:\\';
您也可以使用 proc_open 函数,它可以让您访问标准错误、返回状态码和标准输出。
$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);
与 popen 或 exec 等其他函数相比,proc_open 的有趣之处在于它提供了特定于 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
【讨论】:
【参考方案4】:试试这个:
shell_exec('dir 2>&1');
它在启用 UAC 的 Windows 8.1 64 位上运行良好。
【讨论】:
【参考方案5】:我知道我已经选择了一个答案,因为我必须这样做,但我仍然不明白为什么它不能在我的机器上工作,但我找到了使用另一种方法的后备(使用 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;
希望这对某人有所帮助。
【讨论】:
我认为关键是bypass_shell
参数。据我了解,exec()
在您传递它的命令上调用cmd /c
。无论出于何种原因,它一定找不到这个初始的cmd.exe
文件(可能是因为本地环境变量或其他原因)。通过绕过 PHP 提供的隐式 shell,并明确告诉命令运行“这个特定目录中的这个特定 cmd
shell”,PHP 成功地完成了你告诉它的事情。【参考方案6】:
在花了很多时间和一杯咖啡之后,我也遇到了同样的问题
仅在 Windows 7 中禁用用户帐户控制 (UAC) 短路径:P C:\Windows\System32\UserAccountControlSettings.exe 并选择“从不通知”
php exec() 工作正常!
我使用: Apache 版本:2.2.11 PHP 版本:5.2.8 Windows 7 SP1 32 位
最好的问候! :D
【讨论】:
我现在不记得了,但即使我当时也应该禁用 UAC,但即使是这一点也应该记住。好一个 +1。【参考方案7】:如果您在 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() 返回到原始文件夹。
【讨论】:
【参考方案8】:出于安全考虑,您的服务器可能对“exec”命令具有限制访问权限。在这种情况下,也许您应该联系托管公司以取消此限制(如果有的话)。
【讨论】:
我有本地服务器,据我所知 exec 没有限制【参考方案9】:您可以检查 IIS 用户对 cmd.exe 的权限
cacls C:\WINDOWS\system32\cmd.exe
如果输出中的行类似于 (COMPUTERNAME=您的计算机名称):
C:\WINDOWS\system32\cmd.exe COMPUTERNAME\IUSR_COMPUTERNAME:N
表示用户无权执行cmd。
您可以使用命令添加执行权限:
cacls C:\WINDOWS\system32\cmd.exe /E /G COMPUTERNAME\IUSR_COMPUTERNAME:R
【讨论】:
【参考方案10】:看看 Apache error_log,也许你会发现错误信息。
另外,您可以尝试使用 popen 代替 exec。它提供了更多控制,因为您可以将进程开始与输出读取分开:
$p = popen("dir", "r");
if (!$p)
exit("Cannot run command.\n");
while (!feof($p))
echo fgets($p);
pclose($p);
【讨论】:
他在 Windows 上使用 IIS 服务器。 我试图查找 IIS 日志,但我无法理解它们在 win7 上的位置以上是关于php exec:不返回输出的主要内容,如果未能解决你的问题,请参考以下文章