在shell脚本中将二进制数据转换为十六进制

70

我想将二进制数据转换成十六进制,只需要简单的转换,不需要任何花哨的格式化。使用 hexdump 命令过于复杂,会给我做很多"过度格式化"的处理。我希望能够从 /dev/random 文件中取出 x 字节的数据并将其转换为十六进制数。

最好只使用标准的 Linux 工具,这样我就不需要在每台机器上都安装工具(因为机器数量很多)。


我不得不使用 /dev/urandom,因为 /dev/random 会冻结。 - Aquarius Power
3
@AquariusPower 所说的是,当系统没有足够的随机数据时,/dev/random 会产生随机块,而 /dev/urandom 则不会(如果我没记错,它会循环使用现有数据)。 - davka
9个回答

90

也许可以使用xxd

% xxd -l 16 -p /dev/random
193f6c54814f0576bc27d51ab39081dc

9
xxd 是 vim 的一部分,因此它可能未始安装。 - Håkon A. Hjortland
3
请注意,您可以使用“-c”来更改每行的字节数。不幸的是,您只能将其设置为256,之后需要添加一些换行符。 - Kevin Cox
我只想说谢谢,并且这是我见过最快的生成数据的方法,而不需要过度劳累。我只需要在十六进制中使用32字节的字符串,这个程序就可以轻松解决。time xxd -c 32 -l 1024000000 -ps /dev/urandom 32bytehexnewtest1.txt ; real 0m17.484s - cigol on

45

小心!

hexdumpxxd以不同的字节序列输出结果!

$ echo -n $'\x12\x34' | xxd -p
1234
$ echo -n $'\x12\x34' | hexdump -e '"%x"'
3412

简单解释。大端序和小端序 :D


7
使用echo -n $'\x12\x34' | hexdump -e '/1 "%x"'可以得到相同的字节序。 - user2350426
3
注意!hexdump 命令会去掉前导零。 - Edward Ned Harvey

29

使用 od 命令(适用于GNU系统):

$ echo abc | od -A n -v -t x1 | tr -d ' \n'
6162630a

使用 hexdump 命令(适用于 BSD 系统):

$ echo abc | hexdump -ve '/1 "%02x"'
6162630a

来自十六进制转储、od和hexdump

根据您的系统类型,这两个实用程序中的一个或两个都将可用--BSD系统将od弃用为hexdump,GNU系统则相反。


2
经过4年(其实不算太长),大多数时候我阅读*nix相关的帖子,都会学到一些对我来说非常陌生但又全新的东西。对于od,点赞!我之前从未听说过它,但它非常有用,甚至在Cygwin上也能使用。;-) - Charles Roberto Canato

11

也许你可以自己用C编写一个小工具,并即时编译它:

int main (void) {
  unsigned char data[1024];
  size_t numread, i;

  while ((numread = read(0, data, 1024)) > 0) {
    for (i = 0; i < numread; i++) {
      printf("%02x ", data[i]);
    }
  }

  return 0;
}

然后从标准输入中提供数据:

cat /bin/ls | ./a.out

你甚至可以使用heredoc语法将这个小的C程序嵌入到shell脚本中。


5
这个?用C语言来做有点过了。 - user405725
2
好的,但是你完全控制格式和行为 :-) - Blagovest Buyukliev
那总是一个选项,但我相当确定它之前已经解决了 :) - davka
1
@user405725 - 这似乎是C程序员的最简单解决方案。据我了解,所有其他解决方案都没有对二进制文件进行十六进制编码。我对这项任务变得如此困难感到困惑。难道将二进制文件转换为十六进制真的那么f**k'ing难吗? - jww
2
难以置信,但这很可能是对这个问题最好的答案。 - mnistic

11

所有的解决方法似乎都很难记住或过于复杂。我发现使用 printf 是最简短的方法:

$ printf '%x\n' 256
100

但是正如评论中所指出的那样,这不是作者想要的,为了公平起见,下面是完整的答案。

... 使用上述内容以输出实际的二进制数据流:

printf '%x\n' $(cat /dev/urandom | head -c 5 | od -An -vtu1)

它的作用:

  • printf '%x\n' - 打印一个整数序列,例如printf '%x,' 1 2 3,将会打印1,2,3,
  • $(...) - 这是一种获取某些Shell命令输出并处理它的方法
  • cat /dev/urandom - 它输出随机的二进制数据
  • head -c 5 - 将二进制数据限制在5个字节以内
  • od -An -vtu1 - 八进制转储命令,将二进制转换为十进制

作为测试案例('a'是61进制,'p'是70进制,...):

$ printf '%x\n' $(echo "apple" | head -c 5 | od -An -vtu1)
61
70
70
6c
65

或者想要测试单个二进制字节,可以在输入时给出十进制的61('='字符)来生成二进制数据('\\x%x' 格式可以实现此功能)。上述命令将正确输出 3d(十进制61):

$printf '%x\n' $(echo -ne "$(printf '\\x%x' 61)" | head -c 5 | od -An -vtu1)
3d

@МалъСкрылевъ 我已经更新了我的答案,你是对的,我错过了问题的主要点 - 我可能在寻找在shell中将十进制转换为十六进制的最简单方法,而这个答案在谷歌上排名第一。 - marcinj
嘿,我知道这很老了,但对于第二个代码框,你能不能跳过cat直接使用head呢?printf '%x\n' $(head -c 5 /dev/urandom | od -An -vtu1)或者是有特定的原因需要使用cat命令吗?另外,如果你想要在结尾只用一个新行分隔十六进制,我想到了这个echo $(printf "%x" $(head -c 5 /dev/urandom | od -An -vtu1))虽然我不知道这些的性能成本,主要是为什么我问是否可以去掉cat命令。我对命令行还很陌生,但这些信息都很有帮助。 - cigol on
1
@cigolon 头比猫好。我经常使用 time 命令来测量命令的性能。 - marcinj
这就是我问这个问题的原因。 printf '%x\n' $(cat /dev/urandom | head -c 5 | od -An -vtu1) 相比之下,第二条命令没有使用cat管道,速度更快一些。 printf '%x\n' $(head -c 5 /dev/urandom | od -An -vtu1) - cigol on

7

如果你需要一个没有换行符的大数据流,你可以使用 trxxd(Vim 的一部分)进行逐字节转换。

head -c1024 /dev/urandom | xxd -p | tr -d $'\n'

或者您可以使用 hexdump (POSIX) 进行逐字转换。

head -c1024 /dev/urandom | hexdump '-e"%x"'

请注意,不同之处在于字节序。

2
使用 echo abc | hexdump -e '/1 "%02x"' 命令获取网络顺序和 0x0a 的值为 0。 - user2350426

3

ddhexdump 也可以一起使用:

dd bs=1 count=1 if=/dev/urandom 2>/dev/null  | hexdump -e '"%x"'

谢谢,我开始就是这样做的,但无法让十六进制转储做到我想要的。我很确定它有我需要的选项,但在 man 手册中找不到。 - davka
2
使用以下解决方案(hexdump -e '"%x"'):'\n' -> 'a'(缺少前导'0'),'abcde' -> '6463626165'(字节顺序不正确)。这在非随机数据应用程序中可能非常糟糕! - Håkon A. Hjortland

2

如果你的目标平台不止一个,那么Perl5在可移植性方面可能更有效。它随附于每个Linux发行版和Unix操作系统中。通常可以在容器镜像中找到它,而其他工具(如xxd或hexdump)则不可用。以下是如何使用Perl执行相同操作的方法:

$ head -c8 /dev/urandom | perl -0777 -ne 'print unpack "H*"'
5c9ed169dabf33ab

$ echo -n $'\x01\x23\xff' | perl -0777 -ne 'print unpack "H*"'
0123ff

$ echo abc | perl -0777 -ne 'print unpack "H*"'
6162630a

请注意,这里使用了更多的 slurp,这会导致 Perl 将整个输入读入内存,当输入较大时可能不太优化。

1
这三个命令将打印相同的内容(0102030405060708090a0b0c):
n=12
echo "$a" | xxd -l "$n" -p
echo "$a" | od  -N "$n" -An -tx1 | tr -d " \n" ; echo
echo "$a" | hexdump -n "$n" -e '/1 "%02x"'; echo

假设 n=12$a 是从1到26的字节值:
a="$(printf '%b' "$(printf '\\0%o' {1..26})")"

这可以用来在每个程序中获取$n个随机字节值:

xxd -l "$n" -p                   /dev/urandom
od  -vN "$n" -An -tx1            /dev/urandom | tr -d " \n" ; echo
hexdump -vn "$n" -e '/1 "%02x"'  /dev/urandom ; echo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接