【问题标题】:redirects in php exec are broken in apache + windows7, were working on windowsXPphp exec 中的重定向在 apache + windows7 中被破坏,在 windowsXP 上工作
【发布时间】:2013-08-09 12:26:21
【问题描述】:

我一直在使用 PHP 在 Apache 服务器中执行旧脚本。旧脚本将调试数据写入 STDERR,我一直根据调试设置将其重定向到黑洞或 STDOUT。

PHP 看起来有点像这样:

exec('perl -e "print 10; print STDERR 20" 2>&1', $output);

在 XP 中可靠地工作。我得到了现在运行 windows7 的新硬件,回到这段代码它被破坏了。零输出。返回码 255。不知道为什么。

我能够让它再次运行的唯一方法是删除重定向。哦,重定向在终端盒中仍然可以正常工作。

现在我必须从 apache-error-log 中检索我的调试数据(默认情况下每个 STDERR 输出都在其中),这很不方便,但不是问题。

我只是想了解为什么重定向突然停止工作(也许可以帮助其他遇到同样问题的人)。 apache 是一样的,实际上我只是从旧盒子中复制了 XAMPP 目录。一个错误?系统限制?被操作系统政策禁止?

【问题讨论】:

  • 您可能使用的是 Win XP 32 位,而现在使用的是 Win 7 64 位,这会导致编译为 32 位的 PHP 的某些 dll 发生冲突。重新安装整个 xampp 包,或者构建自己的下载 php 和 apache,但这次适合您的操作系统(如果是 64 位,请安装 64 位 PHP)
  • 我不是 Windows 用户,但经过搜索,我发现 XP 文档中存在 stderr 重定向,但 Win7 缺少该重定向。也许它已被删除?
  • 你的理论听起来很有道理。事实上,我之前使用的是 xp-32,现在使用的是 win7-54。我将不得不看看我是否可以升级 xampp 来测试你的理论。 - 我没有查看 win7 的文档,但我测试了整个命令,因为它应该在“dos-box”中执行,效果很好
  • 似乎只有 PHP 5.5 可用于 64 位,而这些似乎是 Windows 的实验性版本 -> windows.php.net/download/#php-5.5 我需要使用旧版本的 PHP 才能与生产服务器兼容。

标签: php windows apache io-redirection


【解决方案1】:

不要使用exec 和使用文件句柄重定向,而是使用proc_open 并实际捕获stdout 和stderr 的输出。与一些与进程相关的函数不同,proc_ 系列内置于所有版本的 PHP 中,并且可以在 Windows 上正常运行。

为了完整起见,他们的示例的 c&p:

$descriptorspec = array(
   0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
   1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
   2 => array("file", "/tmp/error-output.txt", "a") // stderr is a file to write to
);

$cwd = '/tmp';
$env = array('some_option' => 'aeiou');

$process = proc_open('php', $descriptorspec, $pipes, $cwd, $env);

if (is_resource($process)) {
    // $pipes now looks like this:
    // 0 => writeable handle connected to child stdin
    // 1 => readable handle connected to child stdout
    // Any error output will be appended to /tmp/error-output.txt

    fwrite($pipes[0], '<?php print_r($_ENV); ?>');
    fclose($pipes[0]);

    echo stream_get_contents($pipes[1]);
    fclose($pipes[1]);

    // It is important that you close any pipes before calling
    // proc_close in order to avoid a deadlock
    $return_value = proc_close($process);

    echo "command returned $return_value\n";
}

请务必浏览文档页面上赞成用户提供的注释以了解可能的警告。

【讨论】:

  • 我刚刚尝试了 process_open 并将三个管道作为 r、w、w 传递给它,就像示例中一样。简单的“perl print..”有效,我将 10 个输入到 STDOUT,将 20 个输入到 STDERR。好的!用我的大遗留代码运行同样的事情只是冻结。我可以看到 PERL 在任务管理器中运行,但它没有回来。将打印件放入脚本中,我看到它挂起读取带有“stream_get_contents($pipes[1]);”的 STDOUT。根据您对 cme​​ts 的建议,我将我的 STDERR 管道变成了“a”(甚至是“a+”),并且我按预期恢复了 STDOUT,但 STDERR 不再为零。 又被难住了
  • 有趣的事实:似乎我记得当进程控制使用另一个二进制文件时 php-timeout 确实运行,但无法强制执行。所以现在我有这个执行楔形 PERL 进程的 php 脚本,就是这样。它卡在某个地方,现在将永远运行。对我的桌面来说不是这样的问题。服务器的可怕前景。一旦我用任务管理器杀死 PERL,我就会打印出“哇,你的 php 运行时间太长”的消息,并且脚本在没有机会清理的情况下被杀死。
  • 如果我使用任务管理器杀死 PERL 进程,我会得到 STDERR,但没有 STDOUT。如果我告诉 PERL 不要进行调试打印(这会导致不会将任何内容写入 STDERR),那么一切都会完美无缺。无论顺序如何(首先是 STDERR 或 STDIN 或相反),立即读取和关闭流都不会改变问题。
【解决方案2】:

好的,我得到了(也许至少是我的)解决方案:

  1. 按照查尔斯的建议使用 proc_open
  2. 回到原来的io_redirection原理

直接将内容转储到 STDERR 并通过管道从那里检索它显然不适用于(我的)windows7+PHP 下的代码。简单的例子有效,但对我来说就是这样。

所以在使用2&gt;&amp;1 时破坏了我的 exec() - 最初的问题 - 它与 proc_open() 配合得非常好。问题解决了。

我想知道我现在是否会在运行新代码的 linux 服务器上发现有问题。

小警告:如果您不希望您的代码打印到 STDERR 并且您使用重定向到 null,例如对于生产,在 Windows 中是 2&gt;nul

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-17
    • 2012-08-20
    • 1970-01-01
    • 2014-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-28
    相关资源
    最近更新 更多