Let’s suppose we have a number expressed in given base (i.e. “E0″ in hexadecimal). How can we convert it to decimal using bash or other common Unix tools? And what if we need to perform an any-to-any base conversion?
First way: printf + hexdump
We can tell bash that a given string is just a disguised hexadecimal number by adding “\x” at the top of it and then using the built-in printf function to recover the number:
$ foo="6F"
$foo2="\x"$foo
$ printf "%b" $foo2 | hexdump -e '1/1 "%d" "\n"'
111
$ printf "%b" $foo2 | hexdump -e '1/1 "%o" "\n"'
157
Please notice that %d makes hexdumo to return a signed integer; if you need an unsigned integer just replace it with %u:
$ printf "%b" "\x8F" | hexdump -e '1/1 "%d" "\n"'
-113
$ printf "%b" "\x8F" | hexdump -e '1/1 "%u" "\n"'
143
Second way: bc
bc allows you to set the base of the input numbers and the one for the output (default is decimal) by using respectively ibase and obase variables. So we just need to type:
$ foo="6F"
$ echo "ibase=16; $foo" | bc
111
$ echo "ibase=16; obase=8; $foo" | bc
157
Third way: dc
dc is a little tricky and confusing at the beginning only because it uses RPN (reverse polish notation). That means that if we want to calculate 1+2, we have to type the addends first and then the plus sign:
$ echo "1 2 + p" | dc
3
Nonetheless, dc is another powerful unix tool which can make you solve math or pseudo-math problems without leaving your console.
Just as in our case:
$ foo=6F
$ echo "16 i $foo p" | dc
111
$ echo "16 i 8 o $foo p" | dc
157
First line just sets the input base to hexadecimal (16 i), then pushes 6F into the stack’s head and then prints it. The second in addition to it, sets the output base to the octal one, so that 6F is printed in octal notation.
Fourth way: awk general base converter script
Here is a non-optimised not-much-tested awk script that will convert a number in a given base to any other given base. It should work from base 2 to base 36 (do they really exist?).
#!/usr/bin/awk -f
{
#our general alphabet
alphabet="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
# input base
ibase=$1;
# output base
obase=$2;
#convert third parameter to decimal base
for (i=1;i<=length($3);i++) {
number += substr($3,i,1)*(ibase^(length($3)-i));
}
tmp=number;
#convert "number" to the output base
while (tmp>=obase) {
nut=substr(alphabet,tmp%obase+1,1);
final = nut final;
tmp=int(tmp/obase);
}
final = substr(alphabet,tmp%obase+1,1) final;
printf("%s (b %s) -> %s (b 10) -> %s (b %s)\n",$3,ibase,number,final,obase);
}
Special case: number to char and viceversa
In many low-level languages like C a char is just a byte so you normally think of it just a normal number. On the other hand, bash scripting language a string is generally not a number. However, we can force bash to convert a char to a number and viceversa by using printf:
$ foo=x
echo -n $foo | xxd -c 1 -p
77
printf "%b\n" "\x77"
x