And when I say bash math, I do not mean being mean to math, and I do not mean math as it is done in bash. I mean math as humans do it, but on the bash command line.
I would like to see a set of bash commands that do basic math, so one can do the basic math at the command line without invoking a calculator OR sending data clumsily to a program. Such as: add, which will return the sum of whatever numbers separated by white space (or expressions that can be evaluated to a number) it gets. Similarly, divide (takes two values) and multiply (takes two values). Don't forget squareroot. One command in this toolkit could be simply the command math that takes sequences of numbers or expressions and operators and returns the closest thing to a result possible.








Comments
The expr command already does this. For instance, if I type
> expr 1 + 1
I get the result 2.
It's available in both bash and tcsh.
Posted by: Eric Lund | November 6, 2009 2:15 PM
The thinking behind a shell is that it doesn't "do" a lot, but calls utilities to do things. Hence bc:
echo 'scale=10; 2+sqrt(3)/5' | bc
2.3464101615
Posted by: Liam | November 6, 2009 2:17 PM
Eric,
2 + 3 - 1/4 does not equal 5. Expr says it does.
Liam: That is a very good example of why my utility is needed!
Posted by: Greg Laden | November 6, 2009 2:23 PM
Try this (not-well-tested) function definition in Bash:
$ math(){
echo "scale 10; $*"|bc
}
$ math 1+2 + 1/4
3.2500000000
$
Posted by: Dave X | November 6, 2009 2:46 PM
bash is restricted to integer math AFAIK, so you would just need to put a wrapper around Liam's code.
Probably this is also why eric's example returns 5. It's rounding up 4.75 to an integer.
Posted by: rpsms | November 6, 2009 2:49 PM
And "bc -l" would mean for many cases you could skip the "scale=10;" part.
bc -l loads a "math library" of function defined in bc's own scripting language.
Then a(1)*4 is pi. Want more digits (like 200), then you need scale=200.
$ echo 'scale=200;a(1)*4' | bc -l
3.141592653589793238462643383279502884197169399375105820974944592307\
81640628620899862803482534211706798214808651328230664709384460955058\
223172535940812848111745028410270193852110555964462294895493038196
Posted by: rpenner | November 6, 2009 2:57 PM
@rpenner: ooh, thst's better.
math(){ echo "$MATHOPTIONS;$*"|bc -l; }
PI=`math "a(1)*4"`
echo $PI
math $PI*4^2
Greg, what do you expect from 'divide 1 3' or 'math 1/3'?
Greg, what would you want this to return:
$ divide 1 3
Posted by: Dave X | November 6, 2009 3:10 PM
I would expect "This number can not be computed to the final digit. This is not logical. I am melting down" and then smoke to start coming from behind the monitor.
Or, "0.33333"
Posted by: Greg Laden | November 6, 2009 3:12 PM
I don't know the linux command for smoke, but defining the above wrapper function in your .bashrc should give you some of what you seek.
Posted by: Dave X | November 6, 2009 3:24 PM
if you're not a bash purist,
function add {
python -c "print $1 + $2
}
function divide {
perl -e "print $1/$2"
}
etc. etc...
Posted by: peter | November 6, 2009 4:10 PM
operators: + - / x
usage: foo.sh + 2 2 2 2 2 2 2
error checking: nope
#!/bin/bash
set -f
expression=$@
math_op=$1
expression=${expression//"${math_op}" /}
if [ "$math_op" = "x" ]; then
math_op="*"
fi
expression=${expression// /"${math_op}"}
echo "scale=10; ${expression}" | bc
set +f
Posted by: rpsms | November 6, 2009 4:37 PM
Isn't perl math better than bc math?
Posted by: Greg Laden | November 6, 2009 5:05 PM
Lifehacker just had a post on this topic: http://lifehacker.com/5396183/create-an-awesome-command-line-calculator
The short answer: put this command in your .bashrc (or .bash_profile) file: calc(){ awk "BEGIN{ print $* }" ;}
Then you can use: "calc 99*3/5" to get 59.4
Posted by: Jeff | November 6, 2009 5:28 PM
sweet. very very sweet.
Posted by: Greg Laden | November 6, 2009 5:42 PM
I find Python convenient for a bunch of calculations I frequently need to do. I just put a file, mymod.py in [...]/site-packages. It imports a bunch of functions from math, and includes some functions I wrote for things I often do. I just fire up Python and do a 'from mymod import *'.
Instant personalized math from the CLI. Of course, it's not really bash...
Posted by: gruebait | November 6, 2009 6:06 PM
I use iPython a lot. Especially as my calculator. Check it out. I have it on my jailbroken iPhone and it is fantastic. If you want iPython suped up to nearly the level of Mathematica check out Sage.
Posted by: Clark | November 6, 2009 11:54 PM
calc() { echo $@ | tr x '*' | bc -l; }The -l option of bc is easier than setting the scale variable, and tr allows the use of x for multiplication.
qsum() { sed 's/^[ \t]*//;s/[ \t].*//' | grep . | \ tr - _ | sed '2,$s/$/+/;$s/$/p/' | dc; }This one adds up a column of numbers specified on stdin. It assumes that the first word on each line is a number, and trims away any other data on each line.
Posted by: Ivan | November 7, 2009 4:09 AM
Oh, and for serious computation (but still quick in-and-out from the shell), use PARI/GP. Invoke at the commandline with
(-q suppresses the welcome banner). Loads faster than you can blink, and does some serious shit-- number theory, numerical integration, power series, special functions-- all to arbitrary precision. Documentation includes a PDF reference card so you can quickly get into it.
Posted by: Ivan | November 7, 2009 4:31 AM
rpenner[6]: Then I should define bc as a shortcut to bc -l -c
Ivan, nice
As far as bash purity goes, all of the standard scripting languages are part of bash if they take one liners. That is not impure, it is very pure as part of the bash philosophy. As long as there is a healthy range of widely used and well maintained scripting languages (which currently means sed, awk, perl, python for sure) there is no pressure to add every single bell and whistle to bash. Break it down to small problems, solve one problem at at a time, do it well.
Posted by: Greg Laden | November 7, 2009 8:56 AM
Let's not forget R:
You can do one-liners like this:
alias mult= 'Rscript -e "prod( as.double( commandArgs( TRUE ) ) )"'
OR this one:
alias calc='Rscript -e "eval( parse( text=commandArgs( TRUE ) ) )"'
[note: I've added some spaces to the above code to help it render more suitably in browsers with large font -- gtl]
Posted by: Peter | November 7, 2009 10:32 AM
@Greg
Thanks for fixing the spacing.
I discovered a slight problem with the version of 'calc' that I gave above-- it can be a bit tempermental about spaces. Here's an improved version that should fix that problem and also remove the [1] at the beginning of the output:
$ alias calc='Rscript -e "cat( file=stdout(), eval( parse( text=paste( commandArgs(TRUE), collapse=\"\"))))"'
Posted by: Peter | November 7, 2009 11:07 AM
You might try zsh, it has floats (and a somewhat strange idea what characters a word consists of, unless you clear $WORDCHARS).
Posted by: Ralf Muschall | November 7, 2009 4:43 PM