Vim: 用系统命令替换调用xxd导致转换错误

Vim: calling xxd with system command in substitution results in conversion error

背景是我有一个包含十六进制转储的日志文件,我想用 xxd 转换它以获得漂亮的 ASCII 列,该列显示二进制数据中可能的字符串。

日志文件格式如下:

My interesting hex dump:
00 53 00 6f 00 6d 00 65 00 20 00 74 00 65 00 78
00 74 00 20 00 65 00 78 00 61 00 6d 00 70 00 6c
00 65 00 20 00 75 00 73 00 69 00 6e 00 67 00 20
00 55 00 54 00 46 00 2d 00 31 00 36 00 20 00 69
00 6e 00 20 00 6f 00 72 00 64 00 65 00 72 00 20
00 74 00 6f 00 20 00 67 00 65 00 74 00 20 00 30
00 78 00 30 00 30 00 20 00 62 00 79 00 74 00 65
00 73 00 2e

目视选择十六进制转储并执行 xxd -r -p 然后在结果上执行 xxd -g1 正是我想要的。 但是,由于我要转换的转储数量很多,所以我宁愿自动化该过程。 所以我使用以下替代命令进行转换:

:%s/\(\x\{2\} \?\)\{16\}\_.*/\=system('xxd -g1',system('xxd -r -p',submatch(0)))

表达式匹配日志文件中的整个十六进制转储。匹配作为标准输入发送到 xxd -r -p,其输出用作 xxd -g1 的标准输入。 好吧,至少是这样。

上面的几乎有效。它产生以下结果:

My interesting hex dump:
00000000: 01 53 01 6f 01 6d 01 65 01 20 01 74 01 65 01 78  .S.o.m.e. .t.e.x
00000010: 01 74 01 20 01 65 01 78 01 61 01 6d 01 70 01 6c  .t. .e.x.a.m.p.l
00000020: 01 65 01 20 01 75 01 73 01 69 01 6e 01 67 01 20  .e. .u.s.i.n.g. 
00000030: 01 55 01 54 01 46 01 2d 01 31 01 36 01 20 01 69  .U.T.F.-.1.6. .i
00000040: 01 6e 01 20 01 6f 01 72 01 64 01 65 01 72 01 20  .n. .o.r.d.e.r. 
00000050: 01 74 01 6f 01 20 01 67 01 65 01 74 01 20 01 30  .t.o. .g.e.t. .0
00000060: 01 78 01 30 01 30 01 20 01 62 01 79 01 74 01 65  .x.0.0. .b.y.t.e
00000070: 01 73 01 2e                                      .s..

所有 00 个字节神秘地转变为 01。 它应该产生以下内容:

My interesting hex dump:
00000000: 00 53 00 6f 00 6d 00 65 00 20 00 74 00 65 00 78  .S.o.m.e. .t.e.x
00000010: 00 74 00 20 00 65 00 78 00 61 00 6d 00 70 00 6c  .t. .e.x.a.m.p.l
00000020: 00 65 00 20 00 75 00 73 00 69 00 6e 00 67 00 20  .e. .u.s.i.n.g. 
00000030: 00 55 00 54 00 46 00 2d 00 31 00 36 00 20 00 69  .U.T.F.-.1.6. .i
00000040: 00 6e 00 20 00 6f 00 72 00 64 00 65 00 72 00 20  .n. .o.r.d.e.r. 
00000050: 00 74 00 6f 00 20 00 67 00 65 00 74 00 20 00 30  .t.o. .g.e.t. .0
00000060: 00 78 00 30 00 30 00 20 00 62 00 79 00 74 00 65  .x.0.0. .b.y.t.e
00000070: 00 73 00 2e                                      .s..

我没有得到什么?

当然我可以使用宏和其他方法来执行此操作,但我想了解为什么我的替换命令没有达到我的预期。

编辑:

对于任何想要实现相同目的的人,我提供了适用于整个文件的替换表达式。上面的表达式仅用于测试目的,使用上面的日志文件示例。 下面的那个是执行正确转换的那个,根据 Kent 在他的回答中提供的信息修改。

:%s/\(\(\x\{2\} \)\{16\}\_.\)\+/\=system('xxd -p -r | xxd -g1',submatch(0))

很可能,问题是 system() 中的字符串转换 输入将被 vim 转换为字符串,您的第一个 xxd 命令的输出也是如此。

您可以尝试将十六进制部分提取到文件中。然后:

xxd -r -p theFile|vim -

然后调用 system('xxd -g1', alltext),你也会得到 00 以外的东西。

这与管道 (xxd ...|xxd...) 的工作方式不同。但不幸的是,system() 函数不接受管道。

如果你想修复你的 :s 命令,你需要在第一次 xxd 调用时调用 systemlist() 以获取二进制格式的数据,然后将其传递给第二个xxd:

:%s/\(\x\{2\} \?\)\{16\}\_.*/\=system('xxd -g1',systemlist('xxd -r -p',submatch(0)))

上面的 cmd 将生成 00s。因为没有字符串转换。

然而,当处理纯字符串以外的某些数据格式时,也许我们可以使用过滤器而不是调用 system()。会轻松很多。例如:

2,$!xxd -r -p|xxd -g1