LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Newbie (https://www.linuxquestions.org/questions/linux-newbie-8/)
-   -   Preferred way to do arithmetic in bash ? (https://www.linuxquestions.org/questions/linux-newbie-8/preferred-way-to-do-arithmetic-in-bash-4175432621/)

sylye 10-17-2012 12:11 AM

Preferred way to do arithmetic in bash ?
 
Hi,

Which way is the preferred way you use in doing arithmetic in bash, among expr, $(()) and let ?

I need to do a series of arithmetic with the number with zero padding, like 006767.

I use $(()) and let, they doesn't seem to recognize well the zero padding number. However expr do a good job here. Here is the output:
Code:

[11:48:43 sylye@pc0043 ~]$ echo `expr 006767 + 9 `
6776
[12:04:46 sylye@pc0043 ~]$ let "NEWNO = 0067 + 9"; echo $NEWNO
64
[12:37:05 sylye@pc0043 ~]$ echo $((006767 + 9))
3584
[12:37:26 sylye@pc0043 ~]$

It seems like expr is the better way to do all the arithmetic. Am I right to assume this ?

shivaa 10-17-2012 12:35 AM

Alternatively, you can use awk program for calculations, as follow:
echo | awk '{ print <arithmatic operation> }'
For instance:
echo | awk '{ print 100-100*5/10 }' would result in:
50

chrism01 10-17-2012 12:44 AM

Leading zeros can be tricky; some tools assume this means an octal num.

As per your experiments, 'expr' ignores leading zeroes, 'let' assumes octal; not sure why (()) comes up with that answer... probably something similar.
The technical downside of expr is that its a separate tool, therefore another process being invoked, so a bit more overhead on your system.

Another tool is bc http://linux.die.net/man/1/bc
Code:

echo "0067 + 9"|bc
76


A.Thyssen 10-17-2012 01:40 AM

Really it depends on what you are doing and what you can assume is on the system.

older bash's or scripts that may run under bourne shell can not use $(()).

expr and $(()) is limited to integers only.

awk can do math using floats and is not very big by todays standards, and can handle columns of data (text tables)

perl has access to basically the full math library and can process any sort of data, or even network connections and full databases. It also has even better data storage

bc on the otherhand can handle ANY sized number or any scale (number of decimal places) I have used it with numbers that has over a thousand digits.

Really it is just what your needs and what limitations you have.


Actually there are other solutions too. I have image processing scripts using ImageMagick. As I have to have the "convert" command on the system for the script to work, I often use that as I can't be as equally sure bc, awk, or perl, is available for math processing (such as under cgiwin). Its a odd way of doing it but it removes added script dependancies.

sylye 10-17-2012 01:40 AM

Quote:

Leading zeros can be tricky; some tools assume this means an octal num.
chrism01,

You're correct. It seems both let and $(()) think '006767' is octal, which equal to 3575 in decimal, and 0067 as 55. I mistakenly made a typo in my experiment above, so the correct one should be:

Code:

[14:24:49 sylye@pc0043 ~]$ let "NEWNO = 006767 + 9"; echo $NEWNO
3584
[14:24:54 sylye@pc0043 ~]$ echo $((006767 +9))
3584
[14:24:56 sylye@pc0043 ~]$ let "NEWNO = 0067 + 9"; echo $NEWNO
64
[14:25:04 sylye@pc0043 ~]$ echo $((0067 +9))
64
[14:25:09 sylye@pc0043 ~]$

So the earlier $(()) gave a correct answer.

However, if we are to use let and $(()), how to make them think '006767' as decimal ?

chrism01 10-17-2012 03:37 AM

An interesting qn, here's a few things I found on the net

Code:

tlq=$(echo $t | sed 's/0*//')
echo $t
0067
echo $tlq
67


Code:

printf "%d\n" 00005
5

http://blog.famzah.net/2010/08/07/be...ric-variables/

I've got a feeling you should be able to use bash param fn http://tldp.org/LDP/abs/html/paramet...stitution.html, but I can't find a general soln ie any num of leading zeros, so far...
I'm sure someone else will

sylye 10-17-2012 03:53 AM

hi chrism01,

I found the answer in another place and as well as in the link you have posted :)

Just put 'base#your_number' to tell the bash what base we are actually using, then let and $(()) will know how to do their job:

Code:

[16:43:22 sylye@pc0043 ~]$ echo $(( 10#006767 + 9 ))
6776
[16:43:23 sylye@pc0043 ~]$ echo $((16#006767 + 9))
26480
[16:43:41 sylye@pc0043 ~]$ echo $((8#006767 + 9))
3584
[16:43:47 sylye@pc0043 ~]$  let "NEWNO=10#006767+9"; echo $NEWNO
6776
[16:44:15 sylye@pc0043 ~]$  let "NEWNO=8#006767+9"; echo $NEWNO
3584
[16:44:19 sylye@pc0043 ~]$  let "NEWNO=16#006767+9"; echo $NEWNO
26480

Just found this explained in ABS guide also http://tldp.org/LDP/abs/html/numerical-constants.html

shell script is really so cool !

Thanks for A.Thyssen as well for the ideas :)

chrism01 10-17-2012 04:36 AM

Yeah, but I was thinking a bash param trick was possible and would be more elegant ;)

Actually, Manfred Schwarb's soln there is pretty slick, inc a fail safe for a value that only contains zero.

A.Thyssen 10-17-2012 08:25 PM

Quote:

Originally Posted by chrism01 (Post 4807936)
An interesting qn, here's a few things I found on the net

Code:

tlq=$(echo $t | sed 's/0*//')
echo $t
0067
echo $tlq
67


The above will fail for a number like 8001

The sed should be sed 's/^00*//'
the extra zero is not really needed but it is more exact.

shivaa 10-17-2012 10:45 PM

Quote:

Originally Posted by sylye (Post 4807953)
hi chrism01,

I found the answer in another place and as well as in the link you have posted :)

Just put 'base#your_number' to tell the bash what base we are actually using, then let and $(()) will know how to do their job:

Code:

[16:43:22 sylye@pc0043 ~]$ echo $(( 10#006767 + 9 ))
6776
[16:43:23 sylye@pc0043 ~]$ echo $((16#006767 + 9))
26480
[16:43:41 sylye@pc0043 ~]$ echo $((8#006767 + 9))
3584
[16:43:47 sylye@pc0043 ~]$  let "NEWNO=10#006767+9"; echo $NEWNO
6776
[16:44:15 sylye@pc0043 ~]$  let "NEWNO=8#006767+9"; echo $NEWNO
3584
[16:44:19 sylye@pc0043 ~]$  let "NEWNO=16#006767+9"; echo $NEWNO
26480

Just found this explained in ABS guide also http://tldp.org/LDP/abs/html/numerical-constants.html

shell script is really so cool !

Thanks for A.Thyssen as well for the ideas :)

Can these commands ie. let and then echo, print floating poiint numbers... NO!! It will convert the resulting number in integer and print it. So I think this is not a preferred way to work with. Instead you can use awk code to print full floating point result.

shivaa 10-17-2012 11:25 PM

I have got one solution and it's working perfectly in all environments, and gives result in desired format:

#!/bin/bash
var1=1111688
var2=374335
echo | awk "BEGIN{print 100 - $var2*100/$var1}"
Output: 66.3273


For more details please go though: http://www.linuxquestions.org/questi...wk-4175432706/


All times are GMT -5. The time now is 10:41 AM.