SIGPIPE 13

Shell Calculator

July 26th, 2005

They say that old habits die hard, and the following is probably an example of such.

When I was 12 I bought a Commodore 64 which OS was a BASIC interpreter. One of the commands was print, and if given an arithmic expression, it'd print the result of this expression. Since the print command was typed a lot, one could use a question mark as short-hand.

Since my computer was always on and nearby, I used it as a calculator, typing e.g. ? 7.45*39.

When I was 14 I bought an Amiga, and it was only natural to create an alias for evaluating expressions on ?.

Today I have a Mac, and recently switched to zsh. I've previously done a similar alias/function for tcsh and bash, so today I figured it was time to do it for zsh.

Actually turned out to be rather simple:

alias '?=bc -l <<<'

With zsh, aliases are expanded first, so when writing:

? (546-425)*34

It'll expand to:

bc -l <<< (546-425)*34

The <<< says the next part is a here-string, which is a string that is given to the command as standard input, so this is equivalent to writing:

echo '(546-425)*34' | bc -l

Notice however that in this form, we need to quote the expression, since the parenthesis and asterisk would otherwise cause a syntax error. But with here-strings all characters are taken verbatim (which is to our advantage).

The only limitation with this solution is that we cannot have any spaces in our expression. E.g. ? 42 / 7 would fail.

There is however one thing that bothers me. If you perform e.g. ? 42/7 then it'll output:

6.00000000000000000000

To fix that we'd need to post-process the output from bc, unfortunately the alias system in zsh doesn't support arguments, making it impossible to put anything after our arithmetic expression.

We can however wrap bc in a function that does the post-processing and then let our alias call that function instead, and that's what I ended up doing, so I have the following two lines in my .zshrc:

eval_helper () { bc -l|perl -pe 's/(\.[^0]+)0+$|\.0+$/$1/'; }
alias '?=eval_helper <<<'

[by Allan Odgaard]


10 Responses to “Shell Calculator”

  1. jerik Says:
    October 2nd, 2005 at 17:36

    Just played a little bit in the bash… perhaps thats another solution for you, to manage input with spaces:

    #!/bin/bash
    # file: calculate
    echo $@ | bc -l
    

    Than an alias c="calculate" (cause ? its hard to reach on a german keyboard).

    [jerik@develop] ~ # c 12 -1 *9.24 + 3  
    5.76
    

    cheers

  2. Kelly Miller Says:
    October 24th, 2005 at 03:43

    http://www.nextstudent.com/tools_and_resources/calculators/calculators.asp

  3. Steve Checkoway Says:
    February 5th, 2006 at 13:48

    I really liked this when I first read it so I modified it a bit for my own use (and I use it many many times each day). To deal with the spaces, I modified eval_helper to take arguments:

    function eval_helper()
    {
        bc -ql <<< "$@"|perl -pe 's/(\.[^0]+)0+$|\.0+$/$1/'
    }
    alias '?=eval_helper' 
    
    steve$ ? 12 -1*9.24 + 3 /56
    2.81357142857142857142
    
  4. Steve Checkoway Says:
    February 5th, 2006 at 13:49

    Wow, to doesn't seem to like my function. Let's try that again:

    function eval_helper()
    {
        bc -ql <<< "$@"|perl -pe 's/(\.[^0]+)0+$|\.0+$/$1/'
    }
    alias '?=eval_helper'
    
  5. Steve Checkoway Says:
    February 5th, 2006 at 13:50

    Well, I tried…

  6. here's mine (retry) Says:
    May 19th, 2009 at 12:22

    alias K='noglob kalc'
    
    kalc() {
        zmodload zsh/mathfunc
        print $(( $@ ))
    }
    

  7. Matt Sephton Says:
    August 30th, 2009 at 12:21

    I came across this post after backtracking through the blog since Allan mentioned it in a recent Macromates blog post. I've got a similar old habit but I like to do calculations wherever I am in any app – replacing the expression in place in my current working text.

    Since Mac 10.6 Snow Leopard allows contextual Services, I've rolled a few of my own – using bc as the calculator engine. They all operate on the currently selected text and come in three flavours: Calculate & Replace (my default), Calculate & Append and Calculate & Speak (just for kicks).

    http://www.gingerbeardman.com/services/

    Hopefully they're of use to others.

    (ps: I use awk to strip my trailing zeros as was faster than starting up Perl in my benchmarks)

  8. Matt Sephton Says:
    August 30th, 2009 at 12:22

    Here's the awk command I use to strip trailing zeros:

    awk '{if($0 ~ /\./) sub("\\.*0+$","");print}'

  9. Troy Hakala Says:
    August 30th, 2009 at 16:08

    Not always as useful as a shell command, but Spotlight also supports math: just hit Cmd+Space and type your expression and it'll calculate the result. It's not possible, that I know of, to copy the result, unfortunately.

  10. BecauseRobots Says:
    September 29th, 2009 at 03:50

    If you're using the bash shell (which you probably are) you can even use bash's built in evaluation with something simple like

    function calc() { echo $[$*]; }

    You can't use spaces, but who cares? use it like "calc 2+2*34" for example. ( here is the exponential operator, you can man bash to find the others.)

    From: http://becauserobots.com/2009/09/22/using-linux-worlds-simplest-shell-calculator/


Leave a Reply