【问题标题】:php exec: does not return outputphp exec:不返回输出
【发布时间】: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用户所有可能的权限

标签: php windows iis exec


【解决方案1】:

PHP 手册的相关部分有一些帖子,例如this one

我在使用 PHP exec 命令执行任何批处理时遇到问题 文件。执行其他命令(即“dir”)工作正常)。但如果我 执行了一个批处理文件,我没有收到 exec 命令的输出。

我的服务器设置包括运行 Windows Server 2003 服务器 IIS6 和 PHP 5.2.3。在这台服务器上,我有:

  1. 授予 Internet 用户对 c:\windows\system32\cmd.exe 的执行权限。
  2. 授予所有人->对写入批处理文件的目录的完全控制权。
  3. 授予所有人->对整个 c:\cygwin\bin 目录及其内容的完全控制权。
  4. 授予 Internet 用户“批量登录”权限。
  5. 指定每个正在执行的文件的完整路径。
  6. 测试了这些从服务器上的命令行运行的脚本,它们运行良好。
  7. 确保 %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 上的位置
            猜你喜欢
            • 2023-03-09
            • 2011-08-01
            • 2013-05-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多