【问题标题】:Vim: calling xxd with system command in substitution results in conversion errorVim:用系统命令替换调用 xxd 导致转换错误
【发布时间】:2020-06-11 08:39:44
【问题描述】:

背景是我有一个日志文件,其中包含我想用 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..

我在这里没有得到什么?

当然我可以使用宏和其他方法来做这件事,但我想了解为什么我的替换命令没有达到我的预期。

编辑:

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

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

【问题讨论】:

    标签: vim xxd


    【解决方案1】:

    很可能,问题在于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
    

    【讨论】:

    • 是的,我也很确定字符串转换是罪魁祸首。 :h system 表示如果输入以字符串形式给出,则将其写入文件。我不清楚在这种情况下是否会发生这种情况,以及文件是否是二进制文件。在范围内操作并按照您的建议执行 !xxd -r -p|xxd -g1 工作。不过,我仍然很好奇“字符串转换”在幕后会发生什么。
    • 没有注意到您编辑了答案。使用systemlist 就像一个魅力。实际上使用system('xxd -r -p|xxd -g1') 也有效,尽管我认为它不应该(Xubuntu 18.04 上的 Vim 版本 8.0)。非常感谢您提供最宝贵的意见!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多