如何在Linux shell中将十六进制转换为ASCII字符?

75

假设我有一个字符串5a

这是ASCII字母Z的十六进制表示。

我需要找到一个Linux shell命令,它可以接收一个十六进制字符串并输出该字符串所代表的ASCII字符。

因此,如果我执行以下命令:

echo 5a | command_im_looking_for

我将会看到一个单独的字母Z

Z
14个回答

116

我曾经使用 xxd 实现这个功能:

echo -n 5a | xxd -r -p

然后我意识到在Debian/Ubuntu中,xxd是vim-common的一部分,因此可能不存在于极简系统中。为了避免使用Perl(在我看来也不是最小系统的一部分),我最终使用sed、xargs和printf来实现此目的:

echo -n 5a | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf

大多数情况下,我只想转换几个字节,对于这样的任务来说这是可以的。与ghostdog74的解决方案相比,这种解决方案的优势在于它可以自动地将任意长度的十六进制字符串转换为ASCII字符。使用xargs是因为printf不从标准输入读取。


2
该死,我知道原始答案已经晚了3年,但我不知道xxd的存在。现在我遇到的问题需要不断使用它...从调试器中不断转储字节字符串的十六进制。是的,@Vouze的评论真是毫无意义。 - Andrew
3
与其使用"echo -n 5a | xxd -r -p",我更喜欢使用"xxd -r -p <<< 5a"好处: (1) 需要输入的字符更少(由于只需要键入3次,因此输入速度更快); (2) 只运行一个命令(省去了echo),而不是两个 => 执行更快。 - Kurt Pfeifle
@KurtPfeifle sh: 1: Syntax error: redirection unexpected 缺点:不符合POSIX标准 - josch
@josch:抱歉,我应该提到它需要Bash才能工作。这是Bash中的“here string”(类似于“here document”的概念)。 - Kurt Pfeifle
xxd -r在busybox中不可用,但第二个可以。谢谢 - phil294

62
echo -n 5a | perl -pe 's/([0-9a-f]{2})/chr hex $1/gie'
请注意,这不会跳过非十六进制字符。如果您只想获取十六进制(没有原始字符串中的任何空白等):
echo 5a | perl -ne 's/([0-9a-f]{2})/print chr hex $1/gie'

此外,zshbashecho中本身就支持这个功能:

echo -e '\x5a'

或者在字符类中加入a-fA-F。 - user181548
我通常不是perl的粉丝,但这是它擅长的东西。然而,如果你要转换大量数据,最好编写一个简短的C程序来完成。 - Adam Rosenfield
8
使用perl -lne 'print pack "H*", $_'更容易。 - Randal Schwartz
8
在给出的例子中,如果不使用echo命令的-n开关,则会在输出Z后输出换行符('\x0a'),因此echo -ne '\x5a'是必需的。请注意,我已经尽力使翻译通俗易懂并保持了原意。 - antik
作为对@antik所说的一点补充,可以通过echo -ne '\x5a\xde\x0b\x0d' | xxd来测试是否一切正常。 - Giuseppe Crinò
显示剩余5条评论

16

你可以仅使用echo完成此操作,无需其他步骤。不要忘记添加 "-n",否则将自动换行:

echo -n -e "\x5a"

1
并没有回答上面提出的问题,但是回答了我在谷歌搜索时寻找的问题 :-) (顺便说一句,将输出发送到| hexdump -C有助于识别不需要的换行符等。) - JepZ
这需要在每个十六进制对之前插入\x。示例是5a,但问题是将十六进制字符串转换为多个ASCII字符。 - Lorrin

10
echo -n "5a" | while read -N2 code; do printf "\x$code"; done

这似乎不适用于多字节消息... 当我运行 echo -n "0102abcd" | while read -n 2 code; do printf "\x$code"; done | hexdump输出0201 cdab - John Steel
1
@John Steel 看起来hexdump默认以十六进制数值单词的形式显示数据。尝试这个命令 echo -n "0102abcd" | while read -n 2 code; do printf "\x$code"; done | hexdump -e '/1 "%02x"' - user3132194

5

以下是适用于任何字节数的Python 3单行代码。

十六进制解码

使用strip,这样stdin上可以有换行符。

$ echo 666f6f0a | python3 -c "import sys, binascii; sys.stdout.buffer.write(binascii.unhexlify(input().strip()))"
foo

十六进制编码

$ echo foo | python3 -c "import sys, binascii; print(binascii.hexlify(sys.stdin.buffer.read()).decode())"
666f6f0a

1
只需在解码十六进制时使用print,您就可以看到控制字符、空字符等。例如:'echo 666f6f0a62617200 | python3 -c "import sys, binascii; print(binascii.unhexlify(input()))"' 输出:'b'foo\nbar\x00'' - gaoithe
你可以将其缩短为:python3 -c 'import binascii; print(binascii.unhexlify(input()))' <<< 486921 - Matthias Braun

4

根据你获得“5a”的方式,你可以在其前面添加“\x”,然后将其传递给printf函数:

$ a=5a
$ a="\x${a}"
$ printf "$a"
Z

3
第二行和第三行可以合并为一行:printf "\x${a}" - Dennis Williamson

2
echo 5a | python -c "import sys; print chr(int(sys.stdin.read(),base=16))"

print 更改为 sys.stdout.write 可以防止额外的换行符字符。 - Brent Bradburn
OverflowError: Python int too large to convert to C long - nmz787

2
这是一个纯 Bash 脚本(因为 printf 是 Bash 内置命令):
#warning : spaces do matter
die(){ echo "$@" >&2;exit 1;}

p=48656c6c6f0a

test $((${#p} & 1)) == 0 || die "length is odd"
p2=''; for ((i=0; i<${#p}; i+=2));do p2=$p2\\x${p:$i:2};done
printf "$p2"

如果Bash已经在运行,这应该比启动新进程的任何其他解决方案都要快。

稍微简短一些:for ((i=0; i<${#p}; i+=2));do p2+=\\x${p:$i:2};done - starfry

2

dc可以在不同的数字进制之间进行转换:

$ echo 5a | (echo 16i; tr 'a-z' 'A-Z'; echo P) | dc
Z$

2

有一个简单的 shell 命令 ascii

如果您使用 Ubuntu,请使用以下命令安装:

sudo apt install ascii

那么

ascii 0x5a

将输出:

ASCII 5/10 is decimal 090, hex 5a, octal 132, bits 01011010: prints as `Z'
Official name: Majuscule Z
Other names: Capital Z, Uppercase Z

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