Shell arithmetic and base conversion
The shell provides for a number of basic arithmetic operations, which are useful in scripts. The shell evaluates arithmetic expressions you give it, performing arithmetic expansion, where it replaces the expression with its result. Give arithmetic expressions in this form:
$(( expression )
You can see arithmetic expansion at work by using
echo to display the results at the command line.
Try what's shown in Listing 5 now.
Listing 5. Arithmetic expansion in the Bourne shell
$ echo $((10+40)) 50 $ echo $((5*(3+3))) 30
You can also assign expansions to variables. Try what's shown in Listing 6.
Listing 6. Assigning arithmetic expansions to shell variables
$ myvar = 10 $ echo $myvar 10 $ echo $(($myvar-2)) 8 $ myvar = $(($myvar+5)) $ echo $myvar 15 $ result = $(($myvar-10)) $ echo $result 5 $
Table 2 lists some of the valid operators that might be used between expressions in most Bourne and Bourne-compatible shells. As in the second example above, statements grouped in their own set of parentheses take precedence. In fact, shell arithmetic precedence is generally determined according to the rules of the C language.
Table 2. Shell conditionals
||Less than (1 if true, 0 if false)|
||Less than or equal to (1 if true, 0 if false)|
||Greater than (1 if true, 0 if false)|
||Greater than or equal to (1 if true, 0 if false)|
||Left bitwise shift: shifts the given integer of the first expression to the left by the number of bits of the second expression|
||Right bitwise shift: shifts the given integer of the first expression to the right by the number of bits of the second expression|
Say you have some number but, in your script, you need to work on it in another base. Automating this conversion is done easily with shell arithmetic. One way is to use shell arithmetic to convert a number from a given base to decimal. If a number is given in an arithmetic expansion, it's assumed to be in decimal notation unless it's prefaced by a either a zero -- in which case it's assumed to be in octal -- or 0x -- in which case it's assumed to be in hexadecimal. Type the following to get decimal output for some octal and hex values:
$ echo $((013)) $ echo $((0xA4))
You can also specify any arbitrary base between 2 and 64 with the following format:
Try converting numbers in binary, octal, hex, and other bases to decimal by typing the lines shown in Listing 7 at the shell prompt.
Listing 7. Outputting numbers in arbitrary bases in the shell
echo $((2#1101010)) echo $((8#377)) echo $((16#D8)) echo $((12#10)) echo $((36#ZZYY))
Another trick for doing base conversion in the shell is to use
bc, the arbitrary precision calculator language, which is available on most UNIX installations. Because it lets you specify an output base, this is a good technique for when you need output in something other than decimal.
contain the value of the base used for input and output, respectively. By default, both are set to 10. To perform base conversion, convert one or both of them, and then give it a number. Try it now, as shown in Listing 8.
Listing 8. Performing base conversion with bc
$ bc -ql 10 10 obase=16 10 A ibase=2 10 2 Control-D $
To do a quick base conversion, use
bc in conjunction with
echo to make a quick one-liner, piping the given values to
bc. Type what's shown in Listing 9.
Listing 9. Shell one-liners for bc
$ echo 'obase=16; 47' | bc 2F $ echo 'obase=10; ibase=16; A03' | bc 2563 $
A note of caution: After you set the input base in
bc, all numbers you input to
bc after that are taken in that base, including any number you give to set the output base. So it's better to set the output base first or you might get unexpected results, as shown in Listing 10.
Listing 10. Precedence matters when setting the input and output base
$ echo 'ibase=16; obase=10; A' | bc A $ echo 'ibase=16; obase=A; A' | bc 10 $