Americas

  • United States
sandra_henrystocker
Unix Dweeb

EZ conversions on Unix

How-To
May 20, 20165 mins
Data CenterLinux

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 obaseto 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.

sandra_henrystocker
Unix Dweeb

Sandra Henry-Stocker has been administering Unix systems for more than 30 years. She describes herself as "USL" (Unix as a second language) but remembers enough English to write books and buy groceries. She lives in the mountains in Virginia where, when not working with or writing about Unix, she's chasing the bears away from her bird feeders.

The opinions expressed in this blog are those of Sandra Henry-Stocker and do not necessarily represent those of IDG Communications, Inc., its parent, subsidiary or affiliated companies.