If you're reading this article, odds are good that you're not only familiar with the command line on your computer system, but that you're quite comfortable using it to the exclusion of the graphical interface. I understand—I've been using the command line since that was the only option in the computing world, and even contributed code to BSD Unix, back in the day. There's a lot about modern GUIs that make them superior, but in terms of power, speed, and flexibility, the command line still rocks, and even more so if you can type quickly.
There are features missing from the command line in a typical Unix or Linux system, however, notably a powerful and flexible calculator. You can use expr
or even the shell $(( ))
notation, but they're quite limited, as is immediately obvious when you try to solve 10/3 or anything else that's not simple integer math.
You can use the basic calculator bc, but that's a tool with a language only a grizzled old-school hacker could possibly love, and it's hard to understand its purpose in the system all these years later. Saddled with a clumsy interface and distinct lack of useful command-line options, how bc is still a part of the operating system is a mystery.
Fortunately, we're talking about Linux, which means we can solve the problem by wrapping a better interface around a tool that offers the raw functionality we need—in Linux parlance, a "wrapper program", for obvious reasons.
That's what the Bash shell script calc does—offer a simple, user-friendly command-line calculator. It even has useful defaults so you don't have to remember to set the decimal precision to non-zero before you solve 10/3.
Working with bc
bc is billed as an arbitrary precision binary calculator, but oddly its default behavior is to work with just integer values within an interactive shell that lacks any prompts whatsoever. Here's a typical interaction:
$ bc
1+1
2
10/3
3
quit
Enter a mathematical equation and it solves it, showing the result. But 10/3 is still 3? To fix that, bc users quickly become familiar with scale
, which lets you specify the precision of the result shown; a higher scale offers more digits after the decimal point. As in:
10/3
3
scale=4
10/3
3.3333
scale=20
10/3
3.33333333333333333333
quit
That's really sufficient information to see how you can produce a simple command-line-friendly floating point calculator as a shell script:
#!/bin/sh
cat << EOF | bc
scale=2
$*
EOF
exit 0
The idea is simple: Whatever the user specifies as the argument or arguments to the command, feed it directly to bc, but preface it by setting the scale to 2. In practice, it's already useful:
$ calc '(100/3) * 2 + (11 + 333.5)'
411.16
Not too bad. But let's take this simple idea and turn it into an interactive calculator shell, something where you could leave a window open and any time you encounter an equation simply copy/paste it and quickly solve it.
Calc: The interactive calculator
The small shell script above can be turned into a simple function without much fuss, ending up looking like this:
scriptbc()
{
scale=$1 ; shift
cat << EOF | $bc
scale=$scale
$*
EOF
}
All that you need to remember when invoking this function within the shell script is that the first argument is always the desired scale, otherwise bc definitely gets a bit baffled.
But that's the hard work. Now the main loop is surprisingly succinct:
while read command args
do
case $command
in
quit|exit) exit 0 ;;
help|\?) show_help ;;
scale) scale=$args ;;
*) scriptbc $scale "$command" "$args" ;;
esac
/bin/echo -n "calc> "
done
Not too complicated, and that's with an added help function thrown in, too. Notice that smart way that the Bash shell lets you work with the read
command in the while statement too—read
always breaks down what the user typed in to one word per listed variable, with everything else in the last of the variables given. So read command args
if the user types in 1+1 means command="1+1
", but if the user types in "1 + 1", the command="1
" and args="+ 1
". In both cases, it works fine, but, of course, this is so the user can specify command words, too.
A few extra echo statements to make things pretty and we've got a real working interactive calculator with lots of capabilities, all powered by bc:
$ calc
Calc--a simple calculator. Enter 'help' for help, 'quit' to quit.
calc> help
In addition to standard math functions, calc also supports:
a%b = remainder of a/b
a^b = exponential: a raised to the b power
s(x) = sine of x, x in radians
c(x) = cosine of x, x in radians
a(x) = arctangent of x, in radians
l(x) = natural log of x
e(x) = exponential log of raising e to the x
j(n,x) = Bessel function of integer order n of x
scale N = show N fractional digits (default = 2)
calc> s(1)
.84
calc> 100 + (10 * 3.55)
135.50
calc> 5545 + 11 – 4.55
5551.45
calc> 10 / 3
3.33
calc> quit
Although its interface might be bizarre, bc turns out to have additional tricks up its proverbial sleeve, including the ability for users to set and utilize variables, making it a lot more like a mathematical programming language. The problem is, the way that this script invokes bc once per line, there's no way to retain state between invocations. That means although users can enter statements such as cars=25
, if they then reference that variable in the next line, it'll have vanished from bc's memory.
bc has hidden superpowers, but …
bc also supports a variety of programming constructs, including if
, while
, and for
statements, halts, breaks, continues for loop management, and functions. But let's be candid: If you really want to write a succinct program that solves math equations, there are better choices, ranging from Perl to big, super powerful tools like Matlab.
Although I'm a great fan of the creative spirit behind creeping featurism, there's also something to be said for recognizing the limitations and capabilities of a given program, and instead of spending days making it more sophisticated, just accepting that it can address some—but not all—problems in this arena. Indeed, although bc supports functions, command flow, and variables, I suspect you'd be hard pressed to find a single script on a modern Unix or Linux system that utilized this feature and that it could be stripped out of bc without anyone ever noticing.
Quick, simple solutions for straightforward problems has always been the great strength of the command-line interface and what made the Unix system design so powerful. In the book Wicked Cool Shell Scripts 2nd Edition, my co-author and I explore this concept in extraordinary detail, offering up more than 100 Bash shell scripts to amaze and delight. More fundamentally, however, we're using scripts to add a fresh layer of mortar on the brick wall of the command line, whether you're on a Mac OS command line, a Linux system, or an old-school Unix server.
If you use a command line even sporadically, you'll be surprised by how helpful our collection of scripts, such as calc will prove to you. After all, great edifices are created by little bricks and small trowels of mortar, right?
14 Comments