busybox 头从数据中删除 NUL 的解决方法

busybox 头从数据中删除 NUL 的解决方法

长话短说:最新提供的 busybox (1.23.2)head有问题,并且会很乐意在提供的数据中删除 NUL 字符。替换二进制文件不是一个选择。我在我的脚本中使用head -[nc] -X,有没有办法可以用其他 busybox 提供的工具来模拟它?


不熟悉文件上传和 busybox httpd 处理方式的人可能想阅读在继续之前。

我正在尝试在安装了 Busybox 的嵌入式系统上处理 CGI 脚本收到的数据上传。发送的数据如下:

$ hexdump -C foo.bin
00000000  03 15 20 00 00 00 75 73  74 61 72 30 30 30 2e 30  |.. ...ustar000.0|
00000010  00 11 00                                          |...|
00000013

当通过 POST 请求发送时,处理数据的 CGI 脚本接收以下数据:

$ hexdump -C 24593.tmp 
00000000  2d 2d 2d 2d 2d 2d 2d 2d  2d 2d 2d 2d 2d 2d 2d 2d  |----------------|
00000010  2d 2d 2d 2d 2d 2d 2d 2d  2d 2d 30 65 34 32 32 64  |----------0e422d|
00000020  63 39 65 64 65 32 34 36  34 30 0d 0a 43 6f 6e 74  |c9ede24640..Cont|
00000030  65 6e 74 2d 44 69 73 70  6f 73 69 74 69 6f 6e 3a  |ent-Disposition:|
00000040  20 66 6f 72 6d 2d 64 61  74 61 3b 20 6e 61 6d 65  | form-data; name|
00000050  3d 22 66 69 6c 65 22 3b  20 66 69 6c 65 6e 61 6d  |="file"; filenam|
00000060  65 3d 22 66 6f 6f 2e 62  69 6e 22 0d 0a 43 6f 6e  |e="foo.bin"..Con|
00000070  74 65 6e 74 2d 54 79 70  65 3a 20 61 70 70 6c 69  |tent-Type: appli|
00000080  63 61 74 69 6f 6e 2f 6f  63 74 65 74 2d 73 74 72  |cation/octet-str|
00000090  65 61 6d 0d 0a 0d 0a 03  15 20 00 00 00 75 73 74  |eam...... ...ust|
000000a0  61 72 30 30 30 2e 30 00  11 00 0d 0a 2d 2d 2d 2d  |ar000.0.....----|
000000b0  2d 2d 2d 2d 2d 2d 2d 2d  2d 2d 2d 2d 2d 2d 2d 2d  |----------------|
000000c0  2d 2d 2d 2d 2d 2d 30 65  34 32 32 64 63 39 65 64  |------0e422dc9ed|
000000d0  65 32 34 36 34 30 2d 2d  0d 0a                    |e24640--..|
000000da

从上面检索原始数据很容易:

$ tail -n +5 24593.tmp | head -n -1 | head -c -2 | hexdump -C
00000000  03 15 20 00 00 00 75 73  74 61 72 30 30 30 2e 30  |.. ...ustar000.0|
00000010  00 11 00                                          |...|
00000013

然而,busybox 有一个坏处head,会删除数据中的所有 NUL 字符:

$ /firmware/system/xbin/tail -n +5 24593.tmp | /firmware/system/xbin/head -n -1 | /firmware/system/xbin/head -c -2 | hexdump -C
00000000  03 15 20 75 73 74 61 72  30 30 30 2e 30 11        |.. ustar000.0.|
0000000e

是否有任何其他方法可以使用head -[nc] -Xbusybox 提供的其他工具来模拟(或至少执行工作)行为?


最谨慎的做法是引入/替换具有正确行为的新二进制文件,但由于多种原因(我们不必在此讨论)而无法这样做。

这篇文章似乎主要致力于提及与当前问题无关的细节,但我在这里介绍细节是为了避免XY问题

答案1

如果我正确理解您的问题,您想从包含以下内容的请求中提取上传的文件:

  • 由破折号后跟十六进制数字组成的线;
  • 更多非空行(标题);
  • 空行;
  • 要提取的内容;
  • 换行符;
  • 重复第一行;

其中换行符是 CRLF 序列,内容可以包含任意字节,但标头不包含空字节。

您可以通过两个步骤来完成此操作,而不是依赖headtail解析行:

  1. 确定要提取的字节位置的范围。
  2. 提取这些字节。

第一步,为了避免空字节问题,请使用tr将它们转换为其他内容。然后,您可以使用基于行的工具来检测第一个空行并检测最后一行的开头。我在下面使用 awk,并借此机会也提取文件名。如果你没有 awk 那么你可以使用head, tail, sed, ...

对于第二步,您可以使用dd块大小为 1 的块。这很慢但可靠。

upload=24593.tmp
filename=$(<"$upload" tr '\0' _ | awk '
    {line_start = line_end; line_end += length($0)+1}
    !content_start && /^Content-Disposition:.*filename="/ {
        sub(/.*filename="/, ""); sub(/".*/, "");
        filename = $0
    }
    !content_start && $0=="\r" {content_start=line_end}
    END {print content_start, line_start-2-content_start, filename}
')
skip=${filename%% *}; filename=${filename#* }
count=${filename%% *}; filename=${filename#* }
if [ -z "$filename" ]; then filename=$(mktemp); fi
<$upload dd bs=1 skip="$skip" count="$count" >"$filename"

答案2

Busybox 是高度可配置的,因此答案将取决于编译的内容。以下是一些选项。

catv -v

会将空值显示为“^@”。

split -l 1

将创建文件xaa xab等,每个文件中包含一行。

相关内容