If anybody within earshot of you is struggling to learn sed and ever remarks "This is like learning Klingon! Could they make it any more cryptic?", you can always point them to dc.
dc is the command-line Unix "Reverse-Polish Notation"(RPN) calculator. The name stands for "desktop calculator". OK, so what is a Polish calculator and why would you want to reverse one? The math that you do in school uses infix notation, with the operator between the numbers (3 + 5). Prefix notation puts the operator first (+ 3 5) and is what the Lisp language uses. The prefix notation is known as "Polish notation" after the nationality of Jan Łukasiewicz who invented it. Postfix notation, then, has the operator at the rear (3 5 +), and so is also the reverse of Polish notation.
What's the difference? In computer programming, you have to specify what order you want a multi-part calculation to be in. Everybody is familiar with the old problem of A*B-C. For A=5, B=3, C=2, multiplying first and subtracting second gives you 13, while subtracting first and multiplying second gives you 5. To specify which operations you want performed first, you have to either memorize the complex orders of operations (which change from one language to the next) or use a lot of parenthesis ((A*B)-C) or (A*(B-C)). Hey, that's what everybody complains about in all those languages that use parenthesis!
In dc, we eliminate the problem this way:
dc -e '5 3 * 2 - p'
...gives 13, and
dc -e '5 3 2 - * p'
...gives 5. What's going on here is that it uses a "push-n-pop" stack, a data-storage system familiar to programmers of Assembly and Lisp. When you enter a number, it gets pushed onto the stack. When you enter an operator or a command, it pops the latest affected numbers off the stack, performs the operation, and pushes the answer back onto the stack. The commands (which are all single-letter) have various other effects.
Reading through "info dc" on the command line will give you the commands of dc, but will do little to help you grasp the unfamiliar concepts if you aren't used to using an RPN calculator. So consider this post to be the supplemental reading to the dc info section (or the dc man page).
Let's look at one of the reasons to use dc as opposed to the Bash echo command or Python: you can set the floating-point precision easily. The command to do that is "k" (first thing you thought of, right? It stands for krecision!) and it takes the most-recently-pushed number as an argument. So you can say "2 k" to set the precision to two decimal places for dollars-and-cents operations. While we're at it, let's introduce the "v" command, which computes a square root. This makes sense if you've seen it in ASCII art:
___ v 2
So, the dc way to get the square root of two to ten decimal places is:
ß dc -e '10k2vp'
which gives back 1.4142135623 and returns you to a prompt, lickety-split. We could also make it less confusing by using spaces:
ß dc -e '10 k 2 v p'
That's "10... set this as the precision... 2... calculate this number's square root... print the result".
The only time you need to use spaces is to separate two numbers (so 10 and 30 don't become 'one-thousand-and-thirty'). For that matter, you can also run dc interactively. The "-e" thingie says to evaluate the expression (and exit immediately). But check out this session:
...the "q" is the command to quit. Since you can also omit spaces in interactive mode, we could also go:
Story problem: Johnny is taking a contract to write 20 articles @ 600 words each, for which he intends to charge 2¢ per word. How much money will the client pay?
ß dc -e '2k20 600*0.02*p'
And we're going to add one more command: "f" dumps the whole dang tootin' main stack to the screen, instead of just printing the first value.
dc also has a memory feature. You can call the memory 'R' (register?) and this is of course another stack. So you can store a value for later use. Pop a value off of the main stack and push it onto the R stack with "sR" and pop the value back off the 'R' stack and push it back onto the main stack with "LR". To copy the value off the register stack (as opposed to moving it) you say "lR". By the way, registers can have ANY single-letter name you want! But they have to be initialized first before they are used (with a command like "sR"). Puzzled frowns? Here, my pupils:
dc: register 'Q' (0121) is empty
Let's experiment with the previous calculation, using this new knowledge:
What we did: the previous word-count problem, pushed it to Register, copied it back, printed the stack to show where we are, multiplied the Register times 15% and pushed it onto the stack and showed the stack again, then subtracted the second value from the first value and showed the stack again.
Story problem #2: Johnny is taking a contract to write 20 articles @ 600 words each, for which he intends to charge 2¢ per word. Johnny's agent charges 15% commission for finding him these jobs. What is Johnny's take-home before taxes?
ß dc -e '2k20 600*0.02*sRlRlR0.15*-p'
Feel free to indulge in some primal scream therapy, not only at Johnny's ridiculously low income, but at the fantastically terse command he used to figure it. The other beauty of dc is that it is a relatively powerful calculator (supporting +*-/ (standard four functions) % (modulus) ~ (division and remainder) ^ (exponent) | (modular exponent) and v (square root)) which can be included in a script or command line. It can also do base-conversions on-the-fly with the "i" (input radix) and "o" (output radix) commands.
So, 256 in hexadecimal is:
ß dc -e '16o256p'
While hex FFFF in decimal is:
ß dc -e '16iFFFFp'
So, it has many uses in scripting.
Hope you got something out of this little tutorial, and now for some links:
Wikipedia dc page
dc man page online, hosted by "Bash Cures Cancer".
Advanced Bash Scripting Guide (aka "the Bible") uses dc in a couple of script examples about half-way down. Also shows some of bc, dc's friendly companion.
Everything2's node on dc It gets around to the Unix command at the middle-to-bottom. Notable for having FizzBuzz in dc (horrors!!!)
blog comments powered by Disqus