如何使用Sed替换数据中的字节序列?

时间:2016-03-03 18:56:03

标签: string bash replace sed makefile

我在Makefile中有这个规则,用 CR LF 替换|||(三个管道符; hex 7c 7c 7c) NUL (回车+换行+空;十六进制0d 0a 00):

rom.hex: rom.txt  
    hexdump -C rom.txt | cut -c10-60 > rom.hex
    sed -i -e 's/  / /g' rom.hex
    sed -i -e 's/7c 7c 7c/0d 0a 00/g' rom.hex

这在某些时候有效 - 但是,如果hexdump的输出将7c 7c 7c序列分成两行,则它与sed不匹配。

替换必须与匹配的长度相同,以便不移动后续字节。

3 个答案:

答案 0 :(得分:3)

在转换为十六进制之前,您可以先进行替换:

rom.hex: rom.txt
    sed -e 's/|||/\r\n\x00/g' $< | hexdump -v | cut -c'10-60' >$@

请注意,反斜杠转义符是GNU sed扩展名,因此这不是一个完全可移植的解决方案。如果需要便携式sed命令,则需要将其放在单独的文件中,因为您不能在命令行参数中包含NUL。必须引用字面换行符:

s/|||/^M\
^@/g

为清楚起见,上面的控制字符是

73 2f 7c 7c 7c 2f 0d 5c  0a 00 2f 67      |s/|||/.\../g|

那么规则就是

rom.hex: rom.txt
    sed -f "transform.sed" $< | hexdump -v | cut -c'10-60' >$@

答案 1 :(得分:1)

- Toby Speight's helpful answer通过使用 GNU sed替换源上的数据来优雅地绕过OP的问题,而无需操作在十六进制上。表示(他的便携式替代方法不适用于BSD sed,但这只是因为替换字符串中的NUL字符。)
- 这个答案的价值在于完全按照陈述
解决OP的问题,特别是使用tr -s '\n' ' ',并提供相对简单的便携式底部的解决方案 - 从字节表示/文本处理的角度来看,它是有意义的 - 请参阅my other answer,了解使用 hexdump格式化选项的更简单的解决方案直接生成所需的输出格式
功能

注意

  • 下面的解决方案将输入的字节值表示转换为行,以便可以强制使用sed来替换值。
  • 如果您确实需要hexdump默认生成的固定宽度多行输出,请将输出通过管道传输到... | fmt -w48

以下命令规范化hexdump -C输出中的所有空格:

hexdump -vC rom.txt | cut -c10-60 | tr -s '\n' ' ' > rom.hex

请注意添加-v 可防止信息丢失
如果没有-v,相邻重复行中的重复项将表示为*。 功能

结果是:

  • 由前导和尾随空格预订的单行

    • 如果要剥离它们,请参阅底部的便携式解决方案。
  • 字节值全部用单个空格分隔;例如: -
    23 21 2f 62 69 6e 2f 62 61 73 68 0a 0a 23 20 23 20 76 3d 24 5f 0a 23 20 23 20 65 63 68 6f 20 22 ...

  • 请注意tr的{​​{1}}(“挤压”)选项,执行翻译后{em> -s\n case,ie),将多次出现的目标字符(在本例中为(空格))折叠成单字符运行。

因此:

  • 不再需要用于规范化内部行空间的中间命令(sed)。

  • 最终sed -i -e 's/ /...命令(sed)可以安全地使用以空格分隔的值作为搜索字符串,而不必担心sed -i -e 's/7c 7c 7c/ ...'中的换行符的位置输出。

简化有空间:

  • 可以使用单个管道 - 无需以中间形式写入文件,并在以后更新。

    • 作为副作用,因为不再需要hexdump -C-i命令变得可移植(符合POSIX);虽然这种形式可以在Linux和BSD / OSX平台上运行,但它仍然不是严格符合POSIX标准,因为sed是一个非标准实用程序;请参阅底部,了解严格符合POSIX标准的解决方案。
  • 特殊hexdump变量make,(第一个)先决条件($<)和rom.hex,目标($@)可以是使用

  • 如果只需要字节值,则不需要rom.txt -C选项;这允许简化hexdump命令,顺便提一下,从输出中删除前导空间(并且还使cut的{​​{1}}选项不必要):

tr
  • -s
    • rom.hex: rom.txt hexdump -v $< | cut -sd' ' -f2- | tr '\n' ' ' | sed 's/7c 7c 7c/0d 0a 00/g' > $@ 表示不会跳过不包含cut -sd' ' -f2-指定的分隔符(分隔符)的行,这会跳过-s的尾随空行(除了字节偏移列以外为空)可以输出。
    • -d使用单个空格作为分隔符将输入拆分为字段。
    • hexdump输出第二个字段到行尾(-d' '),有效地剥离第一个字段({{1}中的输入地址偏移列输出)。

要使命令完全可移植,可以使用POSIX实用程序-f2-代替非标准-实用程序。
此外,额外的hexdump命令用于从输出中删除前导和尾随空格

od
  • hexdump输出十六进制。 (sed)字节(rom.hex: rom.txt od -t x1 -A n -v $< | tr -s '\n' ' ' | sed 's/^ //; s/ $//' | sed 's/7c 7c 7c/0d 0a 00/g' > $@ )跨越多行固定宽度,类似于od -t x1 -A n -v,除了x清空输入地址偏移列; 1确保表示所有字节;没有它,相邻的重复行将表示为hexdump
  • -A n,如上所述,对空白进行规范化,以生成单个长行,其中字节值由单个空格分隔,由单个前导和尾随空格书写。
  • -v删除了前导和尾随空格。
  • 该命令的其余部分与以前一样。

答案 2 :(得分:1)

- 请参阅my other answer了解如何解决问题如上所述或者您是否需要符合 POSIX标准的解决方案。
- 字节表示格式角度来看,这个答案很有用。 功能

注意

  • 下面的解决方案将输入的字节值表示转换为行,以便可以强制使用sed来替换值。
  • 如果您确实需要hexdump默认生成的固定宽度多行输出,请将输出通过管道传输到... | fmt -w48

格式选项传递给hexdump ,可以绕过问题

hexdump -ve '1/1 "%02x "'

直接生成所需的输出格式为单个行(将有一个尾随空格)。

  • -v阻止重复字节缩写为*
  • -e '1/1 "%02x "'
    • 1/1指定将以下格式字符串应用于1个字节大小为1的单位,即每个字节。
    • "%02x "是要应用于每个字节的格式字符串:一个2位十六进制数后跟一个空格。

将所有内容放在一起,使用特殊的make变量$<,(第一个)先决条件(rom.hex)和$@,即目标({{1} }):

rom.txt

替代解决方案使用(非标准)rom.hex: rom.txt hexdump -ve '1/1 "%02x "' $< | sed 's/7c 7c 7c/0d 0a 00/g' > $@ 实用程序;例如xxd,它可以在Linux和BSD / OSX上使用:

hexdump
  • rom.hex: rom.txt xxd -p $< | tr -d '\n' | sed 's/../& /g; s/ $//' | sed 's/7c 7c 7c/0d 0a 00/g' > $@ 打印一个字节值的流,不带分隔符,分成固定长度的行。

  • xxd -p从输出中删除换行符。

  • tr -d '\n'每2个字符后插入一个空格,然后删除该行末尾的尾随空格。

最后,正如Toby Speight在[自清理]评论中指出的那样,您可以使用 GNU sed 's/../& /g; s/ $//' 非标准od选项:

-w
  • rom.hex: rom.txt od -t x1 -A n -w1 -v $< | tr -d '\n' | sed 's/7c 7c 7c/0d 0a 00/g' > $@ 输出十六进制。 (od -t x1 -A n -w1 -v)字节(x)一次1个字节(1); -w1省略了输入地址偏移列; -A n确保表示所有字节;没有它,相邻的重复行将表示为-v
  • *只删除所有换行符,并且由于每行以空格开头,因此结果是一条带有前导空格的长行。