8

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? – void.pointer Nov 21 '19 at 18:48
  • 1
    Do you have awk? – schrodigerscatcuriosity Nov 21 '19 at 19:21
  • 3
    @void.pointer: bc is a "basic shell utility" – jesse_b Nov 21 '19 at 19:24
  • 3
    @void.pointer MINGW should ship with bc because it claims to be POSIX compliant and POSIX requires the environment provide bc – slebetman Nov 22 '19 at 4:47

10 Answers 10

13

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.

| improve this answer | |
  • 4
    +1 This is probably the only case where using a shell variable in awk directly is correct, rather that using the -v option. – user000001 Nov 22 '19 at 8:42
8

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:~$
| improve this answer | |
7

If you have perl:

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

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

| improve this answer | |
  • ok then : perl -pe 's/(.*)/eval $1/e' file – francois P Nov 21 '19 at 19:26
4

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
| improve this answer | |
3

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
| improve this answer | |
3

With Perl:

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

with Python:

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

With Haskell:

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

With calc:

calc -f ex      # apt install apcalc if necessary
| improve this answer | |
2

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)"
| improve this answer | |
2

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.

| improve this answer | |
  • bash does not support floating point arithmetics. – Fiximan Nov 22 '19 at 12:40
  • Yeah what shell are you using that can do this? – jesse_b Nov 22 '19 at 12:54
  • Interesting - I have to admit, I hardly ever use bash, I prefer ksh, but that was for other reasons. – j4nd3r53n Nov 22 '19 at 12:59
1

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.

| improve this answer | |
0

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.)

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.