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


8

仅使用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版本。

Answers:


10

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

如果你想改变2 和第5 字节一个字节序列,就会有以下几个原因不工作:

  • sed适用于文本。如果输入包含NUL字符,不以换行符结尾,两个换行符之间的行数超过LINE_MAX个字节,包含不构成有效字符的字节序列,则取决于sed实现,它将无法在以下位置使用所有。(请注意,GNU sed没有很多限制)。
  • 即使该二进制输入恰好形成有效的文本,也.匹配字符而不是字节,因此可能匹配多个字节。
  • 因为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

或者,您可以使用中间文本表示形式,例如使用vimxxd帮助器:

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

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


谢谢。我正在使用xxd构建解决方法。太好了!两种解决方案都可以在AIX中使用。
卢西亚诺

1

你确定吗 ?通过简单的测试,在我看来,这似乎没有发生(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个字符(空位置),则空值将丢失:

$ 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 

@Luciano查看更新
George Vasiliou

看看我的编辑
卢西亚诺

@Luciano,我也尝试使用sed --posix,它按照我的手册禁用了所有GNU扩展,但是仍然存在空字节....
George Vasiliou

我在Linux中尝试了sed,是的,它似乎可以正常工作。但是我需要让它在AIX中工作。
卢西亚诺

1
@Luciano,当然,我可以理解...不幸的是,我没有AIX可以为您提供帮助,据我所知,似乎没有在线的AIX Shell可以玩...我敢肯定Chazelas先生会为您提供帮助。
乔治·瓦西里乌

0

尝试使用bbe-二进制流的sed克隆:https : //sourceforge.net/projects/bbe/


您能否添加一些支持细节,例如AIX环境中的用户如何使用它?另外,请注意问题是“仅使用shell工具”,因此它们可能无法编译/安装其他工具,
Jeff Schaller

您确定要链接到正确的工具吗?您的链接将转到“ 2013
Ale
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.