Shell脚本中的十六进制转十进制

179

有人可以帮我将十六进制数转换为十进制数吗?需要在shell脚本中进行转换。

例如,我想使用shell脚本将十六进制数字bfca3000转换为十进制。我基本上想要两个十六进制数字的差异。

我的代码如下:

var3=`echo "ibase=16; $var1" | bc`
var4=`echo "ibase=16; $var2" | bc`
var5=$(($var4-$var3))               # [Line 48]

执行时,我得到了这个错误:

Line 48: -: syntax error: operand expected (error token is "-")

另一种方法:https://dev59.com/QnRC5IYBdhLWcg3wMeHf。本质上是相同的工具。也可能是跨站点重复:http://superuser.com/questions/226163/linux-shell-utils-convert-a-list-of-hex-to-list-of-decimals - Ciro Santilli OurBigBook.com
8个回答

428

要将十六进制转换为十进制,可以在shell中或使用外部程序有很多方法:

使用

$ echo $((16#FF))
255

使用

$ echo "ibase=16; FF" | bc
255

使用

$ perl -le 'print hex("FF");'
255

使用 函数:

$ printf "%d\n" 0xFF
255

使用

$ python -c 'print(int("FF", 16))'
255

使用

$ ruby -e 'p "FF".to_i(16)'
255

使用

$ node -e "console.log(parseInt('FF', 16))"
255

使用

$ rhino -e "print(parseInt('FF', 16))"
255

使用

$ groovy -e 'println Integer.parseInt("FF",16)'
255

4
printf 的解决方案是否符合 POSIX 标准?如果是,那就是最好的 :) - Ciro Santilli OurBigBook.com
7
在bash中,您也可以使用$((0xff))来表示16进制数,这种写法类似于C语言的十六进制前缀,但N#显然更通用。 - Ruslan
4
第一个示例容易受到整数溢出错误的影响,例如echo $((077E9F2DBF49D100001#FF))会超出64位整数的极限2^64。bc可以正确地处理这种情况。 - roblogic
将 lspci 转换为 xorg.conf PCI ID,以防万一: lspci -d ::0300 | cut -f1 -d' ' | ( IFS=":." read b d f; printf "PCI:%02d:%02d:%02d\n" 0x$b 0x$d 0x$f ) - Michael Shigorin
~ ➜ node <<< "console.log(0x1f)" 31 - user2240578
看起来很蠢,bc 只接受大写十六进制。所以这个不行:echo "ibase=16; 2f" | bc - Craig McQueen

53

在Linux上处理非常轻量级的嵌入式Busybox版本意味着许多传统命令不可用(bc、printf、dc、perl、python)

echo $((0x2f))
47

hexNum=2f
echo $((0x${hexNum}))
47

感谢Peter Leung提供的解决方案。


3
好的,但要注意整数溢出错误,例如 echo $((0x077E9F2DBF49D100001)) 超出了 64 位整数的限制,即 2^64。bc 可以正确处理这种情况。 - roblogic
确实,echo 有其局限性,而 bc 可以解决这些问题,但是 Busybox 目前在其应用程序列表中并没有包含 bc。希望它能在未来加入。 - Jeroen Wiert Pluimers
表达式中有语法错误(错误标记为“3”)。 - Amruth A

16

使用 shell(bash 或 ksh,不适用于 dash)的另一种方法:

echo $((16#FF))
255

#符号代表什么?还有其他应用或文档可以了解它的使用吗? - user1527227
1
这里提到了:链接。在第6.5节的末尾,它说:“...数字采用[base#]n的形式,其中可选的基数是介于2和64之间的十进制数,表示算术基数,而n是该基数中的一个数字。如果省略了base#,则使用base 10。大于9的数字由小写字母,大写字母,“@”和“_”表示,按照这个顺序。如果基数小于或等于36,则可以互换使用小写和大写字母来表示10到35之间的数字。” - Tomás Fox
1
@hinekyle提供的解决方案,使用echo $((0x2f))dash上确实有效(已在VMware ESXi 6.5更新3的一部分"BusyBox v1.29.3 (2019-05-21 15:22:06 PDT) multi-call binary."上进行了测试)。 - Jeroen Wiert Pluimers

13

在shell中,有各种工具可供您使用。Sputnick根据您最初的问题为您提供了出色的选项概述。他肯定应该得到投票,因为他花费了时间给您提供多个正确答案。

还有一个不在他的列表中:

[ghoti@pc ~]$ dc -e '16i BFCA3000 p'
3217698816

但如果你只想做减法,为什么要改变输入为十进制?

[ghoti@pc ~]$ dc -e '16i BFCA3000 17FF - p 10o p'
3217692673
BFCA1801
[ghoti@pc ~]$ 

dc 命令是“desk calc”,它会像bc一样从stdin获取输入,但不使用“运算顺序”而是使用栈形式的逆波兰表示法。您可以提供要添加到堆栈中的输入,然后提供操作符来弹出堆栈上的项目,并将结果推回到堆栈上。

在上面的命令中,我们有以下内容:

  • 16i -- 让 dc 接受16进制数输入。 不改变输出进制。
  • BFCA3000 -- 您的初始数字
  • 17FF -- 我选定的一个随机16进制数字,用于从您的初始数字中减去
  • - -- 取出我们已经推入栈中的两个数字,从后面一个数字中减去前面一个数字,然后将结果推回到栈中
  • p -- 打印堆栈上最后一个项目。 这不会更改堆栈,因此...
  • 10o -- 告诉 dc 以“10”为基数打印输出,但请记住,我们当前的输入编号方案是十六进制的,因此“10”表示“16”。
  • p -- 再次打印堆栈上的最后一个项目...这次是十六进制。

您可以使用dc构建极其复杂的数学解决方案。 对于shell脚本来说,这是一个不错的工具。


10

在虚线和其他 shell 中,您可以使用

printf "%d\n" (your hexadecimal number)

将十六进制数转换为十进制数。 这不仅适用于bash或ksh。


1
printf "%d" 0xff - lashgar
1
同时,针对使用 printf "%u" 0xff 输出无符号64位值的问题,也提供了解决方案。 - frido

4

当变量为空(或未定义)时,会出现报告的错误:

$ unset var3 var4; var5=$(($var4-$var3))
bash: -: syntax error: operand expected (error token is "-")

这可能是因为给bc赋的值不正确。很可能是bc需要大写字母的值。它需要BFCA3000,而不是bfca3000。在bash中很容易解决,只需使用^^扩展:

var3=bfca3000; var3=`echo "ibase=16; ${var1^^}" | bc`

那么脚本将会变成这个样子:
#!/bin/bash

var1="bfca3000"
var2="efca3250"

var3="$(echo "ibase=16; ${var1^^}" | bc)"
var4="$(echo "ibase=16; ${var2^^}" | bc)"

var5="$(($var4-$var3))"

echo "Diference $var5"

但是没有必要使用 bc [1],因为Bash可以直接执行转换和减法操作:

#!/bin/bash

var1="bfca3000"
var2="efca3250"

var5="$(( 16#$var2 - 16#$var1 ))"

echo "Diference $var5"

[1]注:我假设这些值可以用64位数学表示,因为在您的原始脚本中差异是在bash中计算的。如果以64位编译,Bash仅限于小于((2 ** 63)-1)的整数。这将是与bc不同之处,后者没有此类限制。


4

最简单的方法:

$ echo $[0x3F]
63

2
这种语法被 Chet Ramey 本人不鼓励使用。请使用 $((0x3f)) - Gilles Quénot

2

我有一个方便的脚本在我的 $PATH 上用于过滤类似于 0x13371337"0x1337" 的输入行并将其转换为十进制字符串(为了清晰起见进行了扩展):

#!/usr/bin/env bash

while read data; do
  withoutQuotes=`echo ${data} | sed s/\"//g`
  without0x=`echo ${withoutQuotes} | sed s/0x//g`
  clean=${without0x}
  echo $((16#${clean}))
done

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