编辑包含“\x00”字节的二进制流

编辑包含“\x00”字节的二进制流

仅使用 shell 工具,如何编辑包含 NULL(0x00 字符)的二进制流并在输出流中保留 0x00 字符?

编辑需要将指定位置的一个字符替换为另一个字符(在下面的示例中用字符“|”),如下所示:

dd ibs=1 skip=$offset count=$reglen status=none if=$ARQ |
        sed 's/./\|/2' |
        sed 's/./\|/5' #| more replacements....

但 sed 在替换之前删除了所有 '\0x00' 字符。

编辑 - 使用 @George Vasiliou 测试演示我的环境中的 sed 行为:

$ echo -e "lineA\nlineB\nlineC" | tr '\n' '\0' | od -t x1
0000000 6c 69 6e 65 41 00 6c 69 6e 65 42 00 6c 69 6e 65
0000020 43 00
0000022

$ echo -e "lineA\nlineB\nlineC" | tr '\n' '\0' | sed 's/./|/5' | od -t x1
0000000 6c 69 6e 65 7c 6c 69 6e 65 42 6c 69 6e 65 43
0000017

我的环境是 AIX 7.1,并且那里的 sed 不是 gnu 版本。

答案1

sed是一个文本公用事业。它适用于文本行(由换行符分隔的有限长度的非 NUL 字符(不是字节)序列)。

如果你想改变第2个第5 字节字节序列,由于以下几个原因它不起作用:

  • sed适用于文本。如果输入包含 NUL 字符、不以换行符结尾、两个换行符之间的字节数超过 LINE_MAX、包含不形成有效字符的字节序列(具体取决于实现),则sed它将无法工作全部。 (请注意,GNUsed没有很多这些限制)。
  • 即使该二进制输入碰巧形成有效文本,也.匹配字符而不是字节,因此可能匹配多个字节。
  • 因为 sed 代码针对每个运行线输入的第二个和第五个字符,这将更改每行的第二个和第五个字符,而不是整个输入的第二个和第五个字符。

要将输入视为任意字节数组(没有 NUL 字节限制或长度限制),您可能需要改用perl

 dd.... | perl -0777 -pe 'for $o (1, 4) {substr($_, $o, 1) = "|"}'

例子:

$ printf 'a\0b\0cd' |
>   perl -0777 -pe 'for $o (1, 4) {substr($_, $o, 1) = "|"}' |
>   od -Ax -tx1 -tc
000000  61  7c  62  00  7c  64
         a   |   b  \0   |   d
000006

或者您可以使用中间文本表示,例如使用 的vim帮助xxd程序:

dd... | xxd -p | sed '1s/../7c/2;1s/../7c/5' | xxd -p -r

xxd -p默认情况下,给出每行 60 个字符的十六进制转储。上面我们将第一行的第二个和第五个 2 位十六进制数替换7c为 ASCII 的数字|

答案2

尝试贝贝,一个类似于 sed 的二进制流编辑器。

手册页

bbe 是一个类似于 sed 的二进制文件编辑器。 bbe 不是像 sed 那样按行读取输入,而是从输入流中读取任意块,并对找到的块执行与字节相关的转换。

答案3

你确定吗 ?通过简单的测试,这在我的情况下似乎不会发生(gnu sed 4.2.2)

$ echo -e "lineA\nlineB\nlineC"
lineA
lineB
lineC
$ echo -e "lineA\nlineB\nlineC" |tr '\n' '\0'
lineAlineBlineC
$ echo -e "lineA\nlineB\nlineC" |tr '\n' '\0' |sed 's/./|/5'
line|lineBlineC
# Verification if the nulls are still there:
$ echo -e "lineA\nlineB\nlineC" |tr '\n' '\0' |sed 's/./|/5' |tr '\0' '\n'                                                                                                
line|
lineB
lineC

经过进一步测试,如果替换我的测试中的第 6 个字符(空位置),则 null 将丢失:

$ echo -e "lineA\nlineB\nlineC" |tr '\n' '\0' |sed 's/./|/6' |tr '\0' '\n'
lineA|lineB 
lineC

$ echo -e "lineA\nlineB\nlineC" |tr '\n' '\0' |sed 's/./|/7' |tr '\0' '\n'
lineA
|ineB           
lineC 

相关内容