提取二进制文件中两个匹配模式之间的数据

提取二进制文件中两个匹配模式之间的数据

我正在尝试从二进制文本文件中提取 jpeg 图像。我想提取 0xFF 0xD8(图像开始)和 0xFF 0xD9(图像结束)之间的所有数据。早些时候,我已经成功运行以下命令从单个段落文件received.txt中获取所需的image.jpg:

sed 's/.*\xFF\xD8/\xFF\xD8/; s/\xFF\xD9.*/\xFF\xD9/' received.txt > image.jpg

但是当我尝试对不同的文件运行相同的操作时,它不起作用。我也尝试过使用

sed -n '/\xFF\xD8/,/\xFF\xD9/p' received.txt > temp.txt
sed 's/.*\xFF\xD8/\xFF\xD8/; s/\xFF\xD9.*/\xFF\xD9/' temp.txt > image.jpg

删除匹配行之前或之后的任何行,但没有成功。

虽然文件太大,但我粘贴了以下相关部分的十六进制转储:

0a 55 57 5d 50 cf ff d8 ff fe ff ff ff d9 df 47 fe e7 c9 3b e9 9b 6b 55 c4 57 9b 98 73 fd 15 f7 77 7e f7 95 dd 55 f7 55 05 cc 55 97 55 dd 62 d1 1f 51 ef f1 ef fb e9 bf ed 5f bf f2 9d 75 af fe 6b fb bf 8f f7 f7 7e ff d3 bf 8e d5 5f df 57 75 fe 77 7b bf d7 af df 5d fb 0a 47 de d5 ff c1 23 9b 20 08 20 65 3c 06 83 11 05 30 50 a0 20 55 20 84 41 04 c2 59 50 89 64 44 44 10 05 20 87 28 1d a9

在这种情况下,所需输出的十六进制转储为:

ff d8 ff fe ff ff ff d9

更新

在尝试解决该问题时,我发现 sed 命令会删除匹配模式之前或之后的所有字符,直至非 ASCII 字符 (0x80 - 0xFF),但不会超出该非 ASCII 字符。举个例子,如果我们尝试:

echo 55 57 5d 50 cf 50 65 7f ff d8 ff fe ff ff ff d9 | xxd -r -p | sed 's/.*\xFF\xD8/\xFF\xD8/' > output

输出的十六进制转储可以看作:

xxd output

这是:

55 57 5d 50 cf ff d8 ff fe ff ff ff d9

可以看出,非 ASCII 字符和匹配模式之间的字符被删除,但非 ASCII 字符之前的字符未被删除。


替代解决方案(不完美)

我使用以下命令在一定程度上解决了该问题:

sed 's/\xFF\xD8/\x0A\xFF\xD8/; s/\xFF\xD9/\xFF\xD9\x0A/' received.txt > temp.txt

然后运行以下命令(如果 0xFF 0xD8 和 0xFF 0xD9 之间没有换行符 (0x0A),该命令将起作用):

sed -n '/\xFF\xD8/{/\xFF\xD9/p}' temp.txt > image.jpg

但如果 image.jpg 文件为空(执行上述命令后),则运行以下命令:

sed -n '/\xFF\xD8/,/\xFF\xD9/p' temp.txt > image.jpg

这些命令将完成所需的工作,只不过它将 0x0A 放在 image.jpg 文件的末尾(即,在 0xFF 0xD9 之后)。就我而言,它没有造成任何问题,因为 JPEG 文件会自动丢弃 0xFF 0xD9 标记后的数据。

当 @chaos 提出一个完美的解决方案时,我被困在“如果图像文件为空”条件的实现上。所以,我现在正在遵循他的解决方案。非常感谢@chaos!


笔记:

以下是如何从十六进制转储中获取实际数据,您可以通过管道将其传输到 sed 命令:

echo 0a 55 57 5d 50 cf ff d8 ff fe ff ff ff d9 df 47 fe e7 c9 3b e9 9b 6b 55 c4 57 9b 98 73 fd 15 f7 77 7e f7 95 dd 55 f7 55 05 cc 55 97 55 dd 62 d1 1f 51 ef f1 ef fb e9 bf ed 5f bf f2 9d 75 af fe 6b fb bf 8f f7 f7 7e ff d3 bf 8e d5 5f df 57 75 fe 77 7b bf d7 af df 5d fb 0a 47 de d5 ff c1 23 9b 20 08 20 65 3c 06 83 11 05 30 50 a0 20 55 20 84 41 04 c2 59 50 89 64 44 44 10 05 20 87 28 1d a9 | xxd -r -p

您可以通过以下方式查看文件的十六进制转储:

xxd file.txt

答案1

使用您的示例数据并grep激活 Perl 正则表达式 (PCRE) ( -P):

grep -oP '\xFF\xD8.*\xFF\xD9' input >image.jpeg

-o标志表示grep仅打印匹配的部分。之后的测试看起来很有希望:

$ file image.jpeg
image.jpeg: JPEG image data

编辑:如果上述不起作用,而且必须如此sed,我们必须将数据转换为文本:

hexdump -ve '1/1 "%.2X"' input | sed 's/.*\(FFD8.*FFD9\).*/\1/' | xxd -r -p >image.jpeg
  • 文件hexdumpinput转换为与您问题中的序列类似的序列。
    • -e指定格式
    • 1/1表示应用格式 1 次(迭代计数),后面1指定/每次迭代要解释的字节数(字节计数)。
    • %.2X格式为:两位数的十六进制值。
  • 然后从转储中 sed删除前后的所有内容FFD8FFD9
    • 括号\(...\)指定我们要保存以供以后使用的子模式
    • 将所有内容替换为\1,这是上面子模式的内容。
  • 至少,xxd将十六进制转储反转为二进制格式。

当使用您问题中的示例时,测试成功:

$ echo 0a 55 57 5d 50 cf ff d8 ff fe ff ff ff d9 df 47 fe e7 c9 3b e9 9b 6b 55 c4 57 9b 98 73 fd 15 f7 77 7e f7 95 dd 55 f7 55 05 cc 55 97 55 dd 62 d1 1f 51 ef f1 ef fb e9 bf ed 5f bf f2 9d 75 af fe 6b fb bf 8f f7 f7 7e ff d3 bf 8e d5 5f df 57 75 fe 77 7b bf d7 af df 5d fb 0a 47 de d5 ff c1 23 9b 20 08 20 65 3c 06 83 11 05 30 50 a0 20 55 20 84 41 04 c2 59 50 89 64 44 44 10 05 20 87 28 1d a9 | \
  xxd -r -p | \
  hexdump -ve '1/1 "%.2X"' | \
  sed 's/.*\(FFD8.*FFD9\).*/\1/' | \
  xxd -r -p >image.jpeg
$
$ file image.jpeg
image.jpeg: JPEG image data
$ xxd image.jpeg
0000000: ffd8 fffe ffff ffd9                      ........

答案2

只是想为 @chaos 解决方案添加更多内容

hexdump -ve '1/1 "%.2X "' input | sed 's/.*\(FF D8.*FF D9\).*/\1/' | xxd -r -p > image.jpeg

我刚刚在%.2Xand 之后、FFD8and之间添加了空格FFD9。这是为了避免匹配移位的模式,例如:

0f fd 80 ... 0f fd 90

相关内容