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 是 批处理文件的实际系统路径(即调用 真实路径())。

execshell_exec 手册页上还有更多内容。也许跟随他们会得到它并为你工作。

【讨论】:

我会尝试按照这个步骤,但这不是批量执行。【参考方案2】:

您从诸如dir 之类的命令获得零输出的主要原因是因为dir 不存在。它是命令提示符的一部分,这个特定问题的解决方案很好,在这个页面的某个地方。

您可以通过按WIN + R 并输入dirstart 来查找此问题 - 出现错误消息!

无论这听起来多么有悖常理,我发现在 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:不返回输出的主要内容,如果未能解决你的问题,请参考以下文章

如何将php shell的exec输出转换为utf8

php执行shell不阻塞方法

如何让exec自己运行,php页面继续运行,让PHP不再阻塞

php 中 怎么让curl_exec 以数组形式返回

PHP中的curl_exec

PHP exec等待返回键