xDroid's Blog

19 xxd (从新手到菜鸟的Linux教程)

今天讲个神器 xxd 很好奇五个小时的高铁上能写多少文章出来/滑稽

为什么要讲这个 xxd 呢?因为简直就是 Linux 命令行工具中的扫描电镜一般的存在(这个比喻……),可以用它来“透视”文件的内容,进行任意的修改操作(不过可能不是你们想象的那种……“修改”……)。准确的说是进行 16 进制的查看与修改,有点像 WinHex 这一类软件,只是 xxd 实在太小啦(18k)。

我就知道不搞个大新闻你们是不会点进来看的/滑稽

还是先看下手册里的描述吧:

XXD(1) General Commands Manual

NAME
xxd - make a hexdump or do the reverse.

SYNOPSIS
xxd -h[elp]
xxd [options] [infile [outfile]]
xxd -r[evert] [options] [infile [outfile]]

DESCRIPTION
xxd creates a hex dump of a given file or standard input. It can also convert a hex dump back to its original binary form. Like uuencode(1) and uudecode(1) it allows the transmission of binary data in a `mail-safe’ ASCII representation, but has the advantage of decoding to standard output. Moreover, it can be used to perform binary file patching.

手册上说 xxd 主要还是一个十六进制转储工具(转储……好像是微软喜欢用的一个词……)。xxd 常常是搭配 vim 一起使用 食用 的,不过考虑到看本系列的人不一定会用 vim 或者是 emacs 邪教成员的……我还是先讲讲命令本身的打印用法吧。


接着上次的两个文件来

$ xxd file1
00000000: 736f 6d65 2063 6f6e 7465 6e74 206f 6620  some content of
00000010: 6669 6c65 310a                           file1.

xxd 的输出主要是三个部分:左边的地址区域,中间的 hex dump ,和右边的原始内容。

  • 左边显示的地址,当然也是十六进制的。默认的话一行 dump 16个字节,所以地址正好加 0x10
  • 中间是经过 hex dump 后以十六进制显示的文件内容。注意到这个文件是 ascii 编码的,所以所有的字节的值都不大于 0xff
  • 右边则是原文内容,如果是一些非文本文件,遇到不可打印的字符时候 xxd 会输出 . 作为代替

还记不记得上次我们说,如果 xargs 读入的、准备作为参数的内容如果有空格,为了避免出现歧义因此需要加上 -0 开关?(不记得的看这里)那么一个很自然的问题就是 find . -name 'file*' -print0 到底输出了啥呢?我们不妨来试试看

$ find . -name 'file*' -print | xxd 
00000000: 2e2f 6669 6c65 2032 0a2e 2f66 696c 6531  ./file 2../file1
00000010: 0a                                       .

$ find . -name 'file*' -print0 | xxd
00000000: 2e2f 6669 6c65 2032 002e 2f66 696c 6531  ./file 2../file1
00000010: 00                                       .

上面一个是一般的输出,而下面是特意标注了 -print0 的输出。乍一看,似乎两段输出没什么不一样的嘛;不过第一段输出在第 8 个和最后一个字节是 0x0a ,而第二段同样位置是 0x00

ascii 表里 0x0a 是_换行_的意思(这里换行符被归为不可打印字符,所以右边一列输出的时候用 . 代替),所以第一段中两个文件名用换行符 \n 分割,与第二段是不同的。


如果 xxd 只能做 hex dump ,也就不够神奇了;但是 xxd 可以做反向 dump ,这就很“魔法”了(手册里作者原话)。
怎么 revert dump 呢?直接上代码

$ xxd file1 file1.dump
$ cat file1.dump 
00000000: 736f 6d65 2063 6f6e 7465 6e74 206f 6620  some content of 
00000010: 6669 6c65 310a                           file1.
$ vim file1.dump 
# magic editing...
$ cat file1.dump
00000000: 736f 6d65 4d41 4749 4321 6e74 206f 6620  some content of 
00000010: 6669 6c65 310a                           file1.
$ xxd -r file1.dump 
someMAGIC!nt of file1

很简单吧?直接加 -r 参数就能做反向 dump 。所以一般步骤是先 xxd TARGET DUMP_FILE 导出,然后修改 DUMP_FILE ,最后 xxd -r DUMP_FILE NEW_FILE (没有 NEW_FILE 的时候默认输出到标准输出)。


简单说下 vim 集成:

  • 先用二进制方式打开文件 vim FILE -b
  • 在正常模式下输入魔法 :%!xxd
  • 修改
  • 退出到正常模式,继续魔法 :%!xxd -r

-b 选项(即二进制模式)打开文件很重要啊!!!