# How can I evaluate a math equation, one per line in a file?

I have a file with the following:

``````37 * 60 + 55.52
34 * 60 + 51.75
36 * 60 + 2.88
36 * 60 + 14.94
36 * 60 + 18.82
36 * 60 + 8.37
37 * 60 + 48.71
36 * 60 + 34.17
37 * 60 + 42.52
37 * 60 + 51.55
35 * 60 + 34.76
34 * 60 + 18.90
33 * 60 + 49.63
34 * 60 + 37.73
36 * 60 + 4.49
``````

I need to write a shell command or Bash script that, for each line in this file, evaluates the equation and prints the result. For example, for line one I expect to see `2275.52` printed. Each result should print once per line.

I've tried `cat math.txt | xargs -n1 expr`, but this doesn't work. It also seems like `awk` might be able to do this, but I'm unfamiliar with that command's syntax, so I don't know what it would be.

• I neglected to mention this but I'm using MinGW on Windows, and I don't have a `bc` command. I figured just basic shell utilities would get it done but I guess not? Nov 21, 2019 at 18:48
• Do you have `awk`? Nov 21, 2019 at 19:21
• @void.pointer: `bc` is a "basic shell utility" Nov 21, 2019 at 19:24
• @void.pointer MINGW should ship with `bc` because it claims to be POSIX compliant and POSIX requires the environment provide `bc` Nov 22, 2019 at 4:47

This `awk` seems to do the trick:

``````while IFS= read i; do
awk "BEGIN { print (\$i) }"
done < math.txt
``````

From here

Note that we're using `(\$i)` instead of `\$i` to avoid problems with arithmetic expressions like `1 > 2` (`print 1 > 2` would print `1` into a file called `2`, while `print (1 > 2)` prints `0`, the result of that arithmetic expression).

Note that since the expansion of the `\$i` shell variable ends up being interpreted as code by `awk`, that's essentially a code injection vulnerability. If you can't guarantee the file only contains valid arithmetic expressions, you'd want to put some input validation in place. For instance, if the file had a `system("rm -rf ~")` line, that could have dramatic consequences.

• +1 This is probably the only case where using a shell variable in awk directly is correct, rather that using the `-v` option. Nov 22, 2019 at 8:42

here is what I whould do not sure it is the best method

``````bc < toto
``````

depending what you want to do with datas

``````francois@zaphod:~\$ cat > toto
37 * 60 + 55.52
34 * 60 + 51.75
36 * 60 + 2.88
36 * 60 + 14.94
36 * 60 + 18.82
36 * 60 + 8.37
37 * 60 + 48.71
36 * 60 + 34.17
37 * 60 + 42.52
37 * 60 + 51.55
35 * 60 + 34.76
34 * 60 + 18.90
33 * 60 + 49.63
34 * 60 + 37.73
36 * 60 + 4.49
francois@zaphod:~\$ while read ; do echo " \$REPLY" | bc  ; done < toto
2275.52
2091.75
2162.88
2174.94
2178.82
2168.37
2268.71
2194.17
2262.52
2271.55
2134.76
2058.90
2029.63
2077.73
2164.49
francois@zaphod:~\$
``````

without BC command you cannot use decimal values :

``````francois@zaphod:~\$ while read ; do echo \$(( "REPLY" )) ; done < toto
-bash: 37 * 60 + 55.52: syntax error: invalid arithmetic operator (error token is ".52")
francois@zaphod:~\$
``````

If you have perl:

``````perl -ne 'print eval \$_,"\n"' math.txt
``````

(I get 50000 lines per second on my laptop using this).

• ok then : perl -pe 's/(.*)/eval \$1/e' file Nov 21, 2019 at 19:26

With old-good Python:

``````\$ python -c \$'import sys;\nfor line in sys.stdin:print(eval(line))' <math.txt
2275.52
2091.75
2162.88
2174.94
2178.82
2168.37
2268.71
2194.17
2262.52
2271.55
2134.76
2058.9
2029.63
2077.73
2164.49
``````

If you only need the results, I'd got with the answer provided by @francois-p

For fun and games, add `paste` and `sed`:

``````\$ paste <(sed 's/\(\$\)/\1\t=/g' somefile) <(bc < somefile)
37 * 60 + 55.52 =   2275.52
34 * 60 + 51.75 =   2091.75
36 * 60 + 2.88  =   2162.88
36 * 60 + 14.94 =   2174.94
36 * 60 + 18.82 =   2178.82
36 * 60 + 8.37  =   2168.37
37 * 60 + 48.71 =   2268.71
36 * 60 + 34.17 =   2194.17
37 * 60 + 42.52 =   2262.52
37 * 60 + 51.55 =   2271.55
35 * 60 + 34.76 =   2134.76
34 * 60 + 18.90 =   2058.90
33 * 60 + 49.63 =   2029.63
34 * 60 + 37.73 =   2077.73
36 * 60 + 4.49  =   2164.49
``````

directly in bash/ksh (Edit: As it turns out, `bash` can't do this, only `ksh` - thanks for pointing this out):

``````\$ while read l
> do
> echo \$((\$l))
> done <<!
> 37 * 60 + 55.52
> 34 * 60 + 51.75
> 36 * 60 + 2.88
> 36 * 60 + 14.94
> 36 * 60 + 18.82
> 36 * 60 + 8.37
> 37 * 60 + 48.71
> 36 * 60 + 34.17
> 37 * 60 + 42.52
> 37 * 60 + 51.55
> 35 * 60 + 34.76
> 34 * 60 + 18.90
> 33 * 60 + 49.63
> 34 * 60 + 37.73
> 36 * 60 + 4.49
> !
2275.52
2091.75
2162.88
2174.94
2178.82
2168.37
2268.71
2194.17
2262.52
2271.55
2134.76
2058.9
2029.63
2077.73
2164.49
``````

This may require a fairly recent version of your shell - `\$((...))` used to only do integer arithmetics.

• `bash` does not support floating point arithmetics. Nov 22, 2019 at 12:40
• Yeah what shell are you using that can do this? Nov 22, 2019 at 12:54
• Interesting - I have to admit, I hardly ever use `bash`, I prefer `ksh`, but that was for other reasons. Nov 22, 2019 at 12:59

With Perl:

``````perl -ple '\$_=eval' ex
perl -nE 'say eval' ex
``````

with Python:

``````python3 -qi < ex
python3 -qic 'import sys; sys.ps1=""' < ex
``````

``````ghci < ex
ghci < ex | grep -Po '> \S+\$'
``````

With calc:

``````calc -f ex      # apt install apcalc if necessary
``````

Using `awk`/`python`:

``````python -c "\$(awk '{printf "print %s;", \$0}' math.txt)"
``````

`awk` is being used here to format your file into an input that python will accept, then python is doing the work.

Alternatively `perl` can be used in pretty much the same way:

``````perl -le "\$(awk '{printf "print %s;", \$0}' math.txt)"
``````

Directly with `awk`:

``````awk '{ printf "%f\n", \$0 }' math.txt
``````

The `\$0` represents the entire line that is read line by line from the file.

Additionally, it is not susceptible to nasty injections. It will only evaluate a line as a floating point number.

Just feed the mess to `bc -l`, it will return one line result for each line of expression. (The `-l` loads the math library, it also sets `scale` to a higher value and so gives fractional results.)