Converting numbers from decimal to binary to hex to decimal to hex to binary back to decimal might cause some people to, if not pull out their hair, pull up a calculator on their desktops, but Unix users have some other options. And these options can be very helpful in scripts where you might need your numbers in some particular format.

One such option is bc -- the arbitrary precision calculator. Just open a terminal (or use the one you undoubtedly already have open) and try some easy calculations to see how this works.

$ echo "obase=10;ibase=2;101" | bc 5

So, what did we just do? Using **ibase**, we set the base of the number provided (in this case, 101) to 2. This means that we’re telling bc that the number we want converted is currently in binary. We used **obase**to set the base that we want the output to be provided in to 10, so we want to see the number 101 converted to decimal. And, as you can see, it did a good job. Binary 101 is decimal 5.

Note that, if obase is omitted from this expression, the output will default to being expressed in decimal, so we get the same answer as we did above by simply failing to specify the output base.

$ echo "ibase=2;101" | bc 5

If we want to specify that the number to be converted is in hex, on the other hand, we set of input base (ibase) to 16. So, in this example, we want to convert hexadecimal F to base 10.

$ echo "ibase=16; 101" | bc 257 echo "ibase=16; F" | bc 15

And, of course, we can supply much larger numbers – some that almost none of us could convert in our heads.

$ echo "ibase=2;10110101111010111011010110001111010110110001110" | bc 10001198400 $ echo "ibase=16;F0854DEB8A98BC40EDF5173DECA940" | bc 12488549799906882448625558942990113926542

The bc command is handy, but does have some restrictions. For one, it won’t work in any base larger than 16. Hex is the most it can handle. And, if you try to force it, you’re going to see an error like this. And it lets you know that, if you want a base larger than 16, that's too bad. You're going to get 16.

$ echo "ibase=24; 198" | bc Runtime warning (func=(main), adr=6): ibase too large, set to 16 408

For another, it seems to just override instructions at times. Give it a problem that doesn’t make sense – like converting an octal F (which, of course, can’t exist) to decimal and it seems to just pretend you meant hex.

$ echo "ibase=8;F" | bc 15

Make the same kind of mistake with a base 16 number, on the other hand, and it’s going to slap your hand.

$ echo "ibase=16;FGH" | bc (standard_in) 1: illegal character: G (standard_in) 1: illegal character: H

Try a non-standard base smaller than 16 and it will convert for you quite happily. I’m not sure why anyone would want to convert numbers provided in base 11, but it works.

$ echo "ibase=11; 198" | bc 228

Yes, the number 228 is the same as 198 base 11 (8 + 99 + 121). You can check this with the reverse calculation – converting decimal 228 to base 11:

$ echo "obase=11;ibase=10; 228" | bc 198

OK, so that’s a lot of fun, I’m sure. But the syntax is a bit tedious. We can do the same kind of thing using a simple script. In this first one, we convert binary to decimal.

bin2dec =========== #!/bin/bash echo -n "enter a binary number> " read num echo "obase=10;ibase=2;$num" | bc

If we run the script, it prompts for a number and spits out the number in decimal.

$ ./bin2dec enter a binary number> 10101 21

And, once again, if you give it a number that isn’t binary, it just pretends that any digit greater than 1 was really meant to be a 1 and happily hums along.

$ ./bin2dec enter a binary number> 123 7 $ ./bin2dec enter a binary number> 103 5

The same basic script converting binary to hex acts the same way.

$ ./bin2hex enter a binary number> 11111 1F $ ./bin2hex enter a binary number> 1008 9

One way to overcome this problem is to check the value of the number before you run the conversion. In the script below, we check to make sure that the number that should be provided in binary doesn’t include anything other than 0’s and 1’s. If it does, we issue an error message and exit.

#!/bin/bash if [ $# == 1 ]; then num=$1 else echo -n "enter a binary number> " read num fi N=`echo $num | tr -d "0-1"` if [ "$N" != "" ]; then echo $num is not binary exit 1 fi echo "obase=16;ibase=2;$num" | bc

In that script, we simply reject input that isn’t in binary. A similar test for octal would be N=`echo $num | tr -d "0-8"` and hex would be N=`echo $num | tr -d "0-9,A-F"`.

Another option for converting numbers to decimal is shown below. In fact, it only converts numbers to decimal. This command converts FF in base 16 (hex) to decimal.

$ echo "$((16#FF))" 255

This kind of expression can also convert octal and binary numbers to decimal. Just make sure you specify the base before the # sign.

$ echo $((8#77)) 63 $ echo "$((2#111))" 7

And, if you give it a number that isn’t valid, it complains:

$ echo "$((16#F0G))" -bash: 16#F0G: value too great for base (error token is "16#F0G") $ echo "$((2#123))" -bash: 2#123: value too great for base (error token is "2#123")

WRAP UP

Numeric conversions can be a little tricky, but these handy conversion commands can save you a lot of time. This last script takes a file of decimal numbers and converts them to hex, octal and binary.

#!/bin/bash echo dec:hex:oct:bin for num in `cat nums` do hex=`echo "obase=16;ibase=10;$num" | bc` oct=`echo "obase=8;ibase=10;$num" | bc` bin=`echo "obase=2;ibase=10;$num" | bc` echo $num:$hex:$oct:$bin done

Notice that it takes the number that's expressed in hex and, again, quietly ignores our instructions to consider it as a decimal number. Instead of an error, we get a set of hex to hex, octal, and binary conversions.