Quick math on the terminal


Hey there!

Just this past week I stumbled on a HN comment from 2019. The commenter was fiddling with some scripts to perform some simple math directly on the (bash) terminal.

TLDR: Click here to go to the code and skip the backstory.

What the commenter proposed was something like this, based on the dc command (comments are mine).

$ . calc.sh  # Load functionality
$ * 4 5      # Multiply 4 and 5
20
$ / 21 3     # Divide 21 by 3
7

This sounded interesting. I normally open a terminal and invoke Python when I need to do some simple math, and doing it directly without invoking python was appealing.

If I were to do a simple change… some people can find Polish notation intuitive, but for this I'd rather have some more standard notation. Think something like:

$ C 4 * 5
20
$ C 21 / 3
7

You get the point, the command is named C and the arguments should feel as natural as possible.

The simplest way to do this would be to write a simple bash function that invokes the Python interpreter transparently, let's say…

C () {
  python -c "print($*)"
}

But there's a problem! With this we can perform some commands, but products (*) will get expanded into file names:

# Let's just simulate a directory with some files
$ mkdir test
$ cd test
$ touch fname1
$ touch fname2
# Now, if we invoke the function, the '*' will be replaced with file names
$ C 4 * 5
  File "<string>", line 1
    print(4 fname1 fname2 5)
            ^
SyntaxError: invalid syntax

We might thing about disabling the expansion on our C command. But the expansion happens before our command is run, so that won't fix it.

Something that looks like a command, and can do things before it's arguments are expanded is an alias.

__calc () {
  python -c "print($*)"
  set +f  # Re-enable wildcard expansion
}

alias C='set -f; __calc '

This way, the alias runs set -f and disables the expansion before the __calc function arguments get a chance to be evaluated.

$ C 4 * 5
20
$ echo *
fname1 fname2

Ok, now lets import the Python math library

__calc () {
  python -c "from math import *; print($*)"
  set +f  # Re-enable wildcard expansion
}

alias C='set -f; __calc '

And let's we use the library to do some calculations:

$ C sqrt( 999 )
bash: syntax error near unexpected token `('

Well, that's not great. See, the ( character in bash will be understood as the start of a subshell and this, unlike the file name expansion, cannot be disabled.

To be honest, I have yet not found a solution for this, but I can offer you two options:

One, just quote the parameters:

$ C "sqrt( 999 )"
31.606961258558215

The other is to take in the command with other charaters replacing the parens []():

Result

__calc () {
  local __calc_CMD=$(echo "$*"|tr "[]" "()")
  python -c "from math import *; print($__calc_CMD)"
  set +f  # Re-enable wildcard expansion
}

alias C='set -f; __calc '

And with this we have our calculations on the shell:

$ C sqrt [ 3 + [ 3 * 4 ] / 2 ]
3.0

That's it, maybe it can be useful to you. Bye!

comments powered by Disqus