二进制 sed 替换

Posted

技术标签:

【中文标题】二进制 sed 替换【英文标题】:binary sed replacement 【发布时间】:2011-02-05 23:48:44 【问题描述】:

我试图在二进制文件中进行sed 替换,但是我开始相信这是不可能的。基本上我想做的类似于以下内容:

sed -bi "s/\(\xFF\xD8[[:xdigit:]]\1,\\xFF\xD9\)/\1/" file.jpg

我希望实现的逻辑是:通过二进制文件扫描直到十六进制代码FFD8,继续阅读直到FFD9,并且只保存它们之间的内容(丢弃前后的垃圾,但包括@987654325 @和FFD9作为文件的保存部分)

有什么好办法吗?即使不使用sed

编辑:我只是在玩,找到了最干净的方式来做 IMO。我知道这个 grep 语句会表现得很贪婪。

hexdump -ve '1/1 "%.2x"' dirty.jpg | grep -o "ffd8.*ffd9" | xxd -r -p > clean.jpg

【问题讨论】:

在对本质上是随机数据(例如压缩二进制流)的模式进行 grepping 时,请务必注意错误匹配! @snoopy - (1) 有更好的解决方案吗? (2) 如果没有,需要做些什么来改善这种情况?一旦到达“元数据末尾”就停止搜索? 完全取决于您在做什么,但 CPAN 模块 Image::EXIF 允许您提取和更改元数据。可能在这里有用。 仅供参考,这个问题的目的是在 RAID 5 场景中进行手动文件雕刻。抓取条纹和块时,您将在 jpg(或任何其他文件)之前和之后获取数据。这是为了清洁它。 【参考方案1】:

sed 可能能够做到,但可能会很棘手。这是一个执行相同操作的 Python 脚本(请注意,它会就地编辑文件,这是我假设您希望根据您的 sed 脚本执行的操作):

import re

f = open('file.jpeg', 'rb+')
data = f.read()
match = re.search('(\xff\xd8[0-9A-fa-f]+)\xff\xd9', data)
if match:
    result = match.group(1)
    f.seek(0)
    f.write(result)
    f.truncate()
else:
    print 'No match'
f.close()

【讨论】:

如果我想用 /path/sub 替换 C:\path/sub ,请问(\xff\xd8[0-9A-fa-f]+)\xff\xd9这个短语会如何变化?提前感谢您提供可能的答案。 嘿@16851556,你的问题让我很感兴趣。接受挑战。我相信它会变成\x43\x3a\x5c([xX]?[0-9a-fA-F]*)。但是re.search(..., data) 不会进行替换。 ;) 而不是\x43\x3a\x5c([xX]?[0-9a-fA-F]*) 我认为应该是\x43\x3a(\x5c([xX]?[0-9a-fA-F]*))+。每个人的情况都不一样,但如果你的雇主要求你这样做,他们完全是疯了,除非你疯狂地赚钱,否则你必须逃离工作场所。【参考方案2】:

Is there a good way to do this

当然可以,使用 ImageMagick 等图像编辑工具(在网上搜索 linux jpeg、exif 编辑器等),该工具知道如何编辑 jpg 元数据。我相信您可以找到适合您的工具。不要试图以艰难的方式做到这一点。 :)

【讨论】:

同意,这本质上是随机二进制数据,因此在搜索任何 2 字节序列时,您有 1 / (2 ** 16) 的误报率。这大约是每 65K 数据一次。 exiftool (search.cpan.org/dist/Image-ExifTool/exiftool) 是媒体元数据的杀手级应用。 在这里复制我上面的评论:仅供参考,这个问题的目的是在 RAID 5 场景中进行手动文件雕刻。抓取条纹和块时,您将在 jpg(或任何其他文件)之前和之后获取数据。这是为了清洁它。【参考方案3】:

另外,如果没有安装 Python,这个 Perl 可能也可以工作(未测试,警告)...如果没有安装 Python :)

open(FILE, "file.jpg") || die "no open $!\n";
while (read(FILE, $buff, 8 * 2**10)) 
    $content .= $buff;

@matches = ($content =~ /(\xFF\xD8[:xdigit:]+?\xFF\xD9)/g;
print STDOUT join("", @matches);

您需要在open() 调用之后在 DOS 或 VMS 上添加 binmode(FILE); binmode(STDOUT); - 在 Unix 上不需要。

【讨论】:

对不起 DVK - 那是我。在尝试 grep 二进制数据中的短模式时,我自己也被错误所困扰。试想一下这种不匹配的可能性很大,无论是在一个或另一个锚上,还是完全拾取随机的“幻象模式”。我只是认为 OP 迟早可能会以奇怪的乱码 jpeg 告终,不知道为什么!出于同样的原因,也对其他人投了反对票。 如果您说 OP 存在 XY 问题,请在将正则表达式解决方案下载为“坏”之前提供比正则表达式更好的解决方案。如果这个答案有错误,请指出。如果存在正则表达式方法失败的特定模式,请澄清作为答案(再次 XY) 另外,请注意此解决方案不会更改 jpg 文件。仅将找到的字符串(我猜可能是元数据)输出到标准输出以供以后重定向/使用【参考方案4】:

bbe 是“二进制文件的 sed”,对于大型二进制文件应该比 hexdumping/reconstructing 更有效。

其使用示例:

$ bbe -e 's/original/replaced/' infile > outfile

有关man page的更多信息。

【讨论】:

当我在块设备上通过将它重定向回 (通过-o 选项) 到同一设备上使用它时,它修改的文本似乎比我想要的文本多修改。 LVM 甚至无法在编辑后将该设备识别为池的一部分。【参考方案5】:

老问题,但是,

xxd infile | sed 's/xxxx xxxx/yyyy yyyy/' | xxd -r > outfile

可能是最简单和最可靠的解决方案。类似于 OP 中的编辑。

【讨论】:

另外,使用xxd -g 1 infile会在每个字节周围添加一个空格,这样可以更轻松地替换特定的单字节值。 如果xxxx xxxx 跨越换行符,这不会失败吗?【参考方案6】:
sed -i "s/$(python -c "print('\x1f', end='')")/;/g" file

【讨论】:

以上是关于二进制 sed 替换的主要内容,如果未能解决你的问题,请参考以下文章

unix下KSH中shell的SED命令怎样把文件中的NULL替换成空格。 (十六进制码00替换成20)

Linux SED 将 0A 替换为 1A

sed编辑命令

在 z/OS UNIX 文件中替换十六进制字符

Sed 实记 · laoless‘s Blog

AIX中怎么用sed把某字符转换成换行符