173

Can someone help me to convert a hexadecimal number to decimal number in a shell script?

E.g., I want to convert the hexadecimal number bfca3000 to decimal using a shell script. I basically want the difference of two hexadecimal numbers.

My code is:

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

When executing, I get this error:

Line 48: -: syntax error: operand expected (error token is "-")
Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
VenkateshJN
  • 1,904
  • 2
  • 13
  • 16
  • The other way around: http://stackoverflow.com/questions/378829/convert-decimal-to-hexadecimal-in-unix-shell-script. Essentially the same tools. And possible cross site duplicate of: http://superuser.com/questions/226163/linux-shell-utils-convert-a-list-of-hex-to-list-of-decimals – Ciro Santilli OurBigBook.com Sep 12 '14 at 10:01

8 Answers8

417

To convert from hex to decimal, there are many ways to do it in the shell or with an external program:

With :

$ echo $((16#FF))
255

with :

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

with :

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

with :

$ printf "%d\n" 0xFF
255

with :

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

with :

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

with :

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

with :

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

with :

$ groovy -e 'println Integer.parseInt("FF",16)'
255
Gilles Quénot
  • 173,512
  • 41
  • 224
  • 223
52

Dealing with a very lightweight embedded version of busybox on Linux means many of the traditional commands are not available (bc, printf, dc, perl, python)

echo $((0x2f))
47

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

Credit to Peter Leung for this solution.

hinekyle
  • 822
  • 8
  • 16
  • 3
    OK but beware of integer overflow error, e.g. `echo $((0x077E9F2DBF49D100001))` overflows the 64-biit integer limit of 2^64. `bc` handles this properly – roblogic May 02 '18 at 01:00
  • Indeed `echo` has limitations and `bc` solves them, but [Busybox](https://busybox.net) does [currently not have `bc` among the applets](https://web.archive.org/web/20210406101850/https://busybox.net/downloads/BusyBox.html#commands). Hopefully it does in the future. – Jeroen Wiert Pluimers Apr 17 '21 at 13:54
  • syntax error in expression (error token is "3") – Amruth A May 28 '21 at 03:13
16

One more way to do it using the shell (bash or ksh, doesn't work with dash):

echo $((16#FF))
255
Tomás Fox
  • 570
  • 5
  • 8
  • What does the `#` symbol mean? Are there any other applications or documentation to read more about its use? – user1527227 Jul 02 '14 at 02:00
  • 1
    It is mentioned here: [link](http://www.gnu.org/software/bash/manual/bashref.html#Shell-Arithmetic) . At the end of section 6.5 it says: "...numbers take the form [base#]n, where the optional base is a decimal number between 2 and 64 representing the arithmetic base, and n is a number in that base. If base# is omitted, then base 10 is used. The digits greater than 9 are represented by the lowercase letters, the uppercase letters, ‘@’, and ‘_’, in that order. If base is less than or equal to 36, lowercase and uppercase letters may be used interchangeably to represent numbers between 10 and 35." – Tomás Fox Jul 04 '14 at 20:34
  • 1
    The [solution](https://stackoverflow.com/a/22863296/29290) by [@hinekyle](https://stackoverflow.com/users/1810139/hinekyle) using `echo $((0x2f))` does work with `dash` (tested on "BusyBox v1.29.3 (2019-05-21 15:22:06 PDT) multi-call binary." that is part of VMware ESXi 6.5 update 3). – Jeroen Wiert Pluimers Apr 17 '21 at 14:00
13

Various tools are available to you from within a shell. Sputnick has given you an excellent overview of your options, based on your initial question. He definitely deserves votes for the time he spent giving you multiple correct answers.

One more that's not on his list:

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

But if all you want to do is subtract, why bother changing the input to base 10?

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

The dc command is "desk calc". It will also take input from stdin, like bc, but instead of using "order of operations", it uses stacking ("reverse Polish") notation. You give it inputs which it adds to a stack, then give it operators that pop items off the stack, and push back on the results.

In the commands above we've got the following:

  • 16i -- tells dc to accept input in base 16 (hexadecimal). Doesn't change output base.
  • BFCA3000 -- your initial number
  • 17FF -- a random hex number I picked to subtract from your initial number
  • - -- take the two numbers we've pushed, and subtract the later one from the earlier one, then push the result back onto the stack
  • p -- print the last item on the stack. This doesn't change the stack, so...
  • 10o -- tells dc to print its output in base "10", but remember that our input numbering scheme is currently hexadecimal, so "10" means "16".
  • p -- print the last item on the stack again ... this time in hex.

You can construct fabulously complex math solutions with dc. It's a good thing to have in your toolbox for shell scripts.

ghoti
  • 45,319
  • 8
  • 65
  • 104
10

In dash and other shells, you can use

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

to convert a hexadecimal number to decimal. This is not specific to bash or ksh.

novice
  • 400
  • 1
  • 6
  • 13
4

The error as reported appears when the variables are null (or empty):

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

That could happen because the value given to bc was incorrect. That might well be that bc needs UPPERcase values. It needs BFCA3000, not bfca3000. That is easily fixed in bash, just use the ^^ expansion:

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

That will change the script to this:

#!/bin/bash

var1="bfca3000"
var2="efca3250"

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

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

echo "Diference $var5"

But there is no need to use bc [1], as bash could perform the translation and substraction directly:

#!/bin/bash

var1="bfca3000"
var2="efca3250"

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

echo "Diference $var5"

[1]Note: I am assuming the values could be represented in 64 bit math, as the difference was calculated in bash in your original script. Bash is limited to integers less than ((2**63)-1) if compiled in 64 bits. That will be the only difference with bc which does not have such limit.

4

Shortest way yet:

$ echo $[0x3F]
63
Kostas
  • 4,061
  • 1
  • 14
  • 32
2

I have this handy script on my $PATH to filter 0x1337-like; 1337; or "0x1337" lines of input into decimal strings (expanded for clarity):

#!/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
eordano
  • 452
  • 1
  • 5
  • 11