【问题标题】:Why is exec() silently discarding bytes?为什么 exec() 默默地丢弃字节?
【发布时间】:2019-01-07 04:52:55
【问题描述】:

经过一番挣扎,我发现了一些我无法解释/解决的行为,所以在这里寻求帮助。在我们的服务器(Ubuntu 16.04.5 LTS 和 PHP 7.0.30)上,我们正在使用“httpdocs”之外的一些工具,这些工具使用 exec() 调用以获取它们的输出。在这种情况下,它是一个二维码生成器。

但是,某些 QR 码不会显示。我们从该工具获得了一个输出(输出 PNG 的数据),但是当我们将其显示为图像时,它似乎由于某种原因被破坏了。

经过大量调试,我发现结果有时与工具的输出相差 1 或 2 个字节。

我使用下面的二维码 (12345) 进行了最后一次调试,这是一个 240 字节的文件。但是,最终输出时,长度似乎是239字节,所以我们在某个地方丢失了一个字节?

我发现使用exec(),我们正在创建一个输出数组。此数组中不包含尾随空格,例如 \n,因此 implode() 将数组粘合回字符串,如下所示:

<?php

$cmd = 'cat qr.png';
$output = array();
$exit_code = 0;

exec($cmd, $output, $exit_code);

if($exit_code === 0)
{
    header ("Content-type: image/png;");
    print implode(PHP_EOL, $output);
}
die();

但是由于某种原因,在这个过程中丢失了一个字节?我已经使用shell_exec() 尝试了一些其他解决方案(但这里没有 return_var,所以我们无法检查验证过程......

<?php

$command = 'cat qr.png';

$output = shell_exec($command);
if($output !== null)
{
    header ("Content-type: image/png;");
    print $output;
}
die();

...并使用passthru()但是这会直接输出内容,这是不希望的。在实际代码中不可能像下面的示例中那样输出缓冲...)

<?php

$command = 'cat qr.png';
$return_var = 0;

ob_start();
passthru($command, $return_var);

if($return_var === 0)
{
    header ("Content-type: image/png;");
    $output = ob_get_clean();
    print $output;
}
die();

到目前为止,exec()-函数对我们来说一直运行良好,导致 $return_var AND $ 输出,但现在我正在丢失字节。我已经尝试了 PHP_EOL 的一些变体,我们现在将其用作胶水(\n、\r 和 \n\r),但对于前 2 个行尾,我仍然只有 239字节和最后一个字节,我最终得到 241 个字节,所以 1 个字节很多。

为什么我在这里丢失了一个字节?将 $output-array 转换回字符串的正确方法是什么?有没有办法通过内爆数组来取回 240 字节的输出?或者还有其他我还没有找到的函数来执行一个可以给我输出和 return_var 的命令?

【问题讨论】:

  • 我强烈怀疑与行尾相关的一些时髦的事情正在发生。您是否将收到的损坏文件与正确的文件进行了比较?另外,readfile 呢?
  • 字符编码有区别吗?行尾实际上是一样的吗?您是否还尝试查找文件中的任何差异?还;如果图像完全相同,您可能不想在二进制级别上花费太多时间进行调试。
  • 使用 hex-viewer 工具检查这 2 个文件可以为您提供丢失字节的线索。是换行吗?在开始时?在末尾?除了这个字节之外,这两个文件是否相同?
  • 在这种情况下,命令是cat qr.png,但在最终的解决方案中,这类似于/usr/local/bin/tools/generate-qr --input="12345",因此readfile 将不起作用。编码是相同的,但我在某处缺少行尾。区分文件时,我在工作文件(第 3 行)中看到一个倒置的问号,但在损坏的输出中丢失了。当使用 PHP 中的 ord() 时,将倒置的问号粘贴到字符串中,它告诉我它的值是 13 (Enter?)....所以这是我在 exec() 中缺少的换行符,但是怎么做我能正确找回吗?
  • 很抱歉,我无法了解这里发生了什么。在使用换行符作为 Content-type: image/png; 进行内爆后,如何将命令行输出发送到客户端?您的工具是在命令行中以▄█▄ 之类的符号形式将二维码生成为文本输出,还是生成图像二进制文件并将其保存在文件系统中?

标签: php exec shell-exec implode passthru


【解决方案1】:

根据exec 的手动输入,该函数返回输出的最后一行。最后一行是否也包含在$output 的最后一个元素中尚不清楚。

但更重要的是手册指出:

此数组中不包含尾随空格,例如 \n。

我猜这是数组的每个元素(即输出的每行),但无论哪种方式,你的空格在这里都很重要,因为你不处理文本 - 所有字节都很重要在这里有意义。请记住,空格不仅仅意味着\n。例如,它也可以表示\t(一个空格)。

如果一行以 F\t\n(大写 F 或任何其他非空白字符、制表符,然后是换行符)结尾,则两个空白字符都将从末尾删除。在执行implode 操作时,您可能会将\n 放回去,但您永远不会知道被剥离的\t

这里要意识到的关键是 exec 期望处理纯文本而不是原始二进制数据

我建议不要使用cat qr.png,而是使用base64 qr.png 将二进制数据编码为ASCII 字符串,然后在PHP 中使用base64_decode 对其进行解码。如果您实际上不打算使用cat,您仍然可以通过base64 像这样:generate-qr-png |base64。在这种情况下不应该有任何重要的空白,所以exec 不会删除任何内容。

【讨论】:

  • 确实是空格问题。我猜想以制表符或其他东西结尾的行是这里的问题。我希望将exec 的输出重新粘合在一起会产生相同的结果,但两者之间存在差异,会导致文件损坏。 Base64编码和解码它的接缝解决了这个问题,谢谢!使用|base64,我得到了正确的输出。因为它将是嵌入到电子邮件中的图像,所以 base64_decode 甚至不是必需的(在将图像放入 src='data:...' 时进行了编码,但现在已经通过命令完成了: )
  • 听起来应该是这样 :) 如果解决了您的问题,请务必将答案标记为已接受。很高兴我能帮上忙!
猜你喜欢
  • 2011-10-13
  • 2015-09-05
  • 2017-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多