Bash – Do math operation on the numbers typed into command line without call bc

arithmeticbashcommand linezsh

Sometimes I need to run some math operations. I know I can use bc or echo $(( 6/2 )). I have created my own function for bc to read input. But sometimes it takes a long time to type: _bc "6/2". So I have this question:

Is there way to teach zsh/bash how to run math operation for numbers in command line? One example is more than thousands words.

$ 6/2
$ 3.0

It means that zsh/bash must recognize numbers and call i.e. bc.

Best Answer

Shortcut Alt-c (bash)

With bash, using the readline utility, we can define a key sequence to place the word calc at the start and enclose the text written so far into double quotes:

 bind '"\ec": "\C-acalc \"\e[F\""'

Having executed that, you type 23 + 46 * 89 for example, then Alt-c to get:

 calc "23 + 46 * 89"

Just press enter and the math will be executed by the function defined as calc, which could be as simple as, or a lot more complex:

 calc () { <<<"$*" bc -l; }

a (+) Alias

We can define an alias:

alias +='calc #'

Which will comment the whole command line typed so far. You type:

 + (56 * 23 + 26) / 17

When you press enter, the line will be converted to calc #(56 * 23 + 26) / 17 and the command calc will be called. If calc is this function:

 calc(){ s=$(HISTTIMEFORMAT='' history 1);   # recover last command line.
         s=${s#*[ ]};                        # remove initial spaces.
         s=${s#*[0-9]};                      # remove history line number.
         s=${s#*[ ]+};                       # remove more spaces.
         eval 'bc -l <<<"'"$s"'"';           # calculate the line.
       }

 calc(){ s=$(history -1 |                          # last command(s)
             sed '$!d;s/^[ \t]*[0-9]*[ \t]*+ //'); # clean it up 
                                                   # (assume one line commads)
         eval 'bc -l <<<"'"$s"'"';                 # Do the math.
       }

zsh doesn't allow neither a + alias nor a # character.

The value will be printed as:

 $ + (56 * 23 + 26) / 17
 77.29411764705882352941

Only a + is required, String is quoted (no globs), shell variables accepted:

 $ a=23
 $ + (56 * 23 + $a) / 17
 77.11764705882352941176

a (+) Function

With some limitations, this is the closest I got to your request with a function (in bash):

+() { bc -l <<< "$*"; }

Which will work like this:

$ + 25+68+8/24
93.33333333333333333333

The problem is that the shell parsing isn't avoided and a * (for example) could get expanded to the list of files in the pwd.

If you write the command line without (white) spaces you will probably be ok.

Beware of writing things like $(...) because they will get expanded.

The safe solution is to quote the string to be evaluated:

$ + '45 + (58+3 * l(23))/7'
54.62949752111249272462

$ + '4 * a(1) * 2'
6.28318530717958647688

Which is only two characters shorter that your _bc "6/2", but a + seems more intuitive to me.

Related Question