hexdump 踩坑

创建

执行 echo -n 'abcdefghij' | hexdump

期望排列如下:

61 62 63 64 65 66 67 68 69 6a

然而实际结果非常奇怪:

$ echo -n 'abcdefghij' | hexdump
0000000 6261 6463 6665 6867 6a69
000000a

原因是 hexdump 默认是按字(一个字两个字节)展示,并且每个字按小端法排序,如下图:

不得不说这个默认的规则非常抽象,如何让 hexdump 按照我们期望的,一个字节一个字节按输入顺序展示呢?

查看 man 手册,发现 hexdump 提供了一个选项:-C(大写),表示按标准的十六进制外加 ASCII 小窗展示输入的内容:

$ echo -n 'abcdefghij' | hexdump -C
00000000  61 62 63 64 65 66 67 68  69 6a                    |abcdefghij|
0000000a

这便是我们期望的结果了。

可能因为默认的规则太过抽象,hexdump 还提供了一个缩写:hd,hd 等价于 hexdump -C,测试如下:

$ echo -n 'abcdefghij' | hd
00000000  61 62 63 64 65 66 67 68  69 6a                    |abcdefghij|
0000000a

可以看到结果是一样的。

哎,弄一个彩蛋给你!☝️🤓

前面提到 hexdump 还提供了一个缩写 hd,等价于 hexdump -C。

$ type hd
hd is /usr/bin/hd
$ file /usr/bin/hd
/usr/bin/hd: symbolic link to hexdump
$ ls -l /usr/bin/hd
lrwxrwxrwx 1 root root 7  7月18日 12:41 /usr/bin/hd -> hexdump

查看了 hd 的文件类型,竟然是一个指向 hexdump 的符号链接,那这个符号链接又是怎么把 -C 参数带过去的呢?

点击查看答案

打开 util-linux 的源码,发现 hexdump.c 文件中 main 函数调用了 parse_args 函数,parse_args 函数部分源码如下:

int
parse_args(int argc, char **argv, struct hexdump *hex)
{
    // 其他无关代码 ...

    if (list_empty(&hex->fshead)) {
        if (!strcmp(program_invocation_short_name, "hd")) {
            /* Canonical format */
            add_fmt("\"%08.8_Ax\n\"", hex);
            add_fmt("\"%08.8_ax  \" 8/1 \"%02x \" \"  \" 8/1 \"%02x \" ", hex);
            add_fmt("\"  |\" 16/1 \"%_p\" \"|\\n\"", hex);
        } else {
            add_fmt(hex_offt, hex);
            add_fmt("\"%07.7_ax \" 8/2 \"%04x \" \"\\n\"", hex);
        }
    }
    colors_init (colormode, "hexdump");
    return optind;
}

原来 hexdump 判断了 program_invocation_short_name 这个变量名,当它的值为 hd 时做了特殊处理,果然高端的效果只需要朴素的 if。

program_invocation_short_name 的值是怎么来的?

program_invocation_short_name 是一个在 glibc 中定义的全局变量,用于获取当前运行程序的简短名称。这个变量包含了调用程序的基本名称,即路径中最后一个斜杠之后的部分。与之相对的是 program_invocation_name,它包含了完整的程序调用名称,包括路径信息。这两个变量在 errno.h 头文件中声明,并且可以通过定义宏 _GNU_SOURCE 来启用它们的使用,即

#define _GNU_SOURCE
#include <errno.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
    printf("program_invocation_name: %s\n", program_invocation_name);
    printf("program_invocation_short_name: %s\n", program_invocation_short_name);
}

验证:自己随便在其他什么地方建个名字叫 hd 的符号链接指向 /usr/bin/hexdump 应该是一样的效果。

$ ln -s /usr/bin/hexdump ~/hd
$ echo 'abcdefghijk' | /home/cui/hd
00000000  61 62 63 64 65 66 67 68  69 6a 6b 0a              |abcdefghijk.|
0000000c

评论

《 “hexdump 踩坑” 》 有 4 条评论

  1. 太牛逼了!

  2. 芬子 的头像
    芬子

    6,学到了

  3. 手法大帝 的头像
    手法大帝

    牛逼大发了

  4. fernando 的头像
    fernando

    very excellent!
    Estupendo!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

苏ICP备2023046324号-1