为什么 exec() 默默地丢弃字节?
Why is exec() silently discarding bytes?
经过一番挣扎,我发现了一些我无法解释/解决的行为,所以在这里寻求帮助。在我们的服务器上(Ubuntu 16.04.5 LTS with PHP 7.0.30),我们使用了 'httpdocs' 之外的一些工具,这使用 exec()
调用以获取其输出。在这种情况下,它是一个 QRCode 生成器。
但是,某些二维码不会显示。我们从该工具获得了输出(正在输出 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 $输出,但现在我' m 丢失字节。我已经尝试了 PHP_EOL 的一些变体,我们现在用作胶水(\n、\r 和 \n\r),但前两行-结尾,我仍然只有 239 个字节,最后一个字节,我最终有 241 个字节,所以 1 个字节太多了。
为什么我在这里丢失了一个字节?将 $output-array 转换回字符串的正确方法是什么?有没有办法通过内爆数组来取回 240 字节的输出?或者还有其他我还没有找到的函数来执行一个命令,它会给我输出以及 return_var?
根据exec
的手册输入,函数returns最后一行输出。最后一行是否也作为 $output
的最后一个元素包括在内尚不清楚。
但更重要的是,手册指出:
Trailing whitespace, such as \n, is not included in this array.
我猜这是数组的每个元素(即输出的每行),但无论哪种方式,你的 whitespace 在这里都很重要,因为你没有处理文本 - 所有字节在这里都很重要且有意义。请记住,白色 space 不仅仅意味着 \n
。它也可以表示 \t
或 </code>(一个 space),例如。</p>
<p>如果一行以 <code>F\t\n
结尾(大写字母 F,或任何其他非白色space 字符,一个制表符,然后是换行符),两者都是白色space 字符将从末尾删除。在执行 implode
操作时,您可能会将 \n
放回去,但您永远不会知道被剥离的 \t
。
这里要意识到的关键是 exec
期望处理纯文本而不是原始二进制数据。
我建议不要使用 cat qr.png
,而是使用 base64 qr.png
将二进制数据编码为 ASCII 字符串,然后在 PHP 中使用 base64_decode
对其进行解码.如果您不打算在现实中使用 cat
,您仍然可以通过 base64
来传输 QR 生成命令的输出,如下所示:generate-qr-png |base64
。在那种情况下不应该有任何重要的白色space,所以exec
不会破坏任何东西。
经过一番挣扎,我发现了一些我无法解释/解决的行为,所以在这里寻求帮助。在我们的服务器上(Ubuntu 16.04.5 LTS with PHP 7.0.30),我们使用了 'httpdocs' 之外的一些工具,这使用 exec()
调用以获取其输出。在这种情况下,它是一个 QRCode 生成器。
但是,某些二维码不会显示。我们从该工具获得了输出(正在输出 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 $输出,但现在我' m 丢失字节。我已经尝试了 PHP_EOL 的一些变体,我们现在用作胶水(\n、\r 和 \n\r),但前两行-结尾,我仍然只有 239 个字节,最后一个字节,我最终有 241 个字节,所以 1 个字节太多了。
为什么我在这里丢失了一个字节?将 $output-array 转换回字符串的正确方法是什么?有没有办法通过内爆数组来取回 240 字节的输出?或者还有其他我还没有找到的函数来执行一个命令,它会给我输出以及 return_var?
根据exec
的手册输入,函数returns最后一行输出。最后一行是否也作为 $output
的最后一个元素包括在内尚不清楚。
但更重要的是,手册指出:
Trailing whitespace, such as \n, is not included in this array.
我猜这是数组的每个元素(即输出的每行),但无论哪种方式,你的 whitespace 在这里都很重要,因为你没有处理文本 - 所有字节在这里都很重要且有意义。请记住,白色 space 不仅仅意味着 \n
。它也可以表示 \t
或 </code>(一个 space),例如。</p>
<p>如果一行以 <code>F\t\n
结尾(大写字母 F,或任何其他非白色space 字符,一个制表符,然后是换行符),两者都是白色space 字符将从末尾删除。在执行 implode
操作时,您可能会将 \n
放回去,但您永远不会知道被剥离的 \t
。
这里要意识到的关键是 exec
期望处理纯文本而不是原始二进制数据。
我建议不要使用 cat qr.png
,而是使用 base64 qr.png
将二进制数据编码为 ASCII 字符串,然后在 PHP 中使用 base64_decode
对其进行解码.如果您不打算在现实中使用 cat
,您仍然可以通过 base64
来传输 QR 生成命令的输出,如下所示:generate-qr-png |base64
。在那种情况下不应该有任何重要的白色space,所以exec
不会破坏任何东西。