Beginner in shell script - shell

I am a beginner to shell scripting, so to get used to them I am starting off easy scripts. Trying to calculate the rate of interest for a "principal" amount, I wrote the below shell script.
But I am getting the output as:(150000*0.8)/100. I thought I will be getting mathematically solved output which is 1200. (pr=($principal*$rof)/100)
Can anyone help me in this? What mistake I have made?
principal=150000
rof=0.8
pr=($principal*$rof)/100
echo $pr

There are a couple of issues with this piece of code. Assuming you are using bash, the correct way is shown below,
Arithmetic operations are performed with the syntax,
x=$(( a + b ))
So, for your case, it becomes,
pr=$((( principle * rof ) / 100))
It is not possible to perform floating point operations in bash. You can use the unix utility bc for such purposes. In your case,
pr=`bc <<< "( $principle * $rof ) / 100"`
So, your complete code now becomes,
#!/bin/bash
principle=150000
rof=0.8
pr=`bc <<< "( $principle * $rof ) / 100"`
echo $pr

Bash does not support floating point number aritmetic, e.g. see this post
$> principle=150000;rof=8;pr=`expr $principle \* $rof / 1000`;echo $pr
1200

Related

Bash - Calculate Percentage [duplicate]

This question already has answers here:
Calculating rounded percentage in Shell Script without using "bc"
(6 answers)
Closed 3 years ago.
I am trying to calculate on a Linux System.
I do have two different numbers, defined with a variable
$1= 1024
$2= 20
My task is now to calculate how many percent are 20 of 1024. The calculation would be (100/1024*20)
The problem is, that bash always says 0 with this type of code:
echo $((100/$1*$2))
Do anyone have an idea how i can calculate this?
you can do this using bc -l command.
Eg. echo "100/1024*20" | bc -l gives 1.953125
Your attempt didn't work because you are performing integer calculation:
100/1024 = 0 // integer calculation
100/1024 = 0.09765625 // floating point calculation
So, you need to explain in some way that floating point calculation is to be done.
You can do it as follows:
awk 'BEGIN {print (100/1024*20)}'
More examples can be found in this post.
You can tell bc to show results in 2 (or desired number of) decimal places.
Use below command
echo "scale=8; 100 / $1 * $2" | bc
On my computer, it reported something like below:
1.95312500
You can change the number of decimal places by passing correct numebr to 'scale' attribute.

Trying to get an average using the contents of two files

So I have two files in my directory that contain a number in each of them. I want to make a script that calculates the average of these two numbers. How would I write it? Would this be correct?
avg=$((${<file1.txt}-${<file2.txt})/2)
Your example does not work. Furthermore, your formula is probably incorrect. Here are two options without unnecessary cat:
avg=$(( (`<file1.txt` + `<file2.txt`) / 2 ))
or
avg=$(( ($(<file1.txt) + $(<file2.txt)) / 2 ))
I find the first one more readable though. Also be warned: this trivial approach will cause problems when your files contain more than just the plain numbers.
EDIT:
I should have noted that the first syntactical/legacy option which uses the backticks (` `) is no longer recommended and should be avoided. You can read more about the WHY here. Thanks at mklement0 for the link!
EDIT2:
According to Eric, the values are floating point numbers. You can't do this directly in bash because only integer numbers are supported. You have to use a little helper:
avg=$(bc <<< "( $(<file1.txt) + $(<file2.txt) ) / 2")
or maybe easier to understand
avg=$(echo "( $(<file1.txt) + $(<file2.txt) ) / 2" | bc)
For those who might wonder what bc is (see man bc):
bc is a language that supports arbitrary precision numbers with
interactive execution of statements.
Here is another alternative since perl is usually installed by default:
avg=$(perl -e 'print( ($ARGV[0] + $ARGV[1]) / 2 )' -- $(<file1.txt) $(<file2.txt))
You'll want to use a command substitution:
avg=$(($(cat file1.txt)-$(cat file2.txt)/2))
However, Bash is a pretty bad language for doing maths (at least unless it's completely integer maths). You might want to look into bc or a "real" language like Python.

Evaluating a mathematical expression stored as a string, into a single number (bash)

I am working on Mac OSX and using bash as my shell. I currently have a string which I wish want evaluated as a number. When I echo the string I get 1.e8*1.07**100. Is there any way to pass this string on to be evaluated as a number?
The background as to why it is a string to start with is because the expression was built step by step. First 1.e8*1.07**%%d is within the code, then the user inputs an integer to be taken as what 1.07 will be raised to the power of. So in the example above, the user would have input 100, and thus the script is stuck with 1.e8*1.07**100, which is the correct expression I was hoping for, but I would have liked it to be evaluated when I echo the variable where it is store.
Actual important bits of code:
BASE=$(printf '1.e8*1.07**%%d')
#Get user input assigned to pow
NUM=$(printf ${BASE} ${pow})
echo $NUM #1.e8*1.07**100
Thanks for any help you can offer.
[Edit: I would also like to not just echo the answer, but store it as a variable.]
How about:
python -c "print $NUM"
By the way, you could just write
BASE="1.e8*1.07**%d"
(In fact, you don't even need the quotes.)
In most unix* systems you'll find a tool called bc that can perform calculations. You'll might need to rewrite your input though, I thinks it accepts ^ instead of **, and I'm not sure about the 1.e8 notation.
It happens that perl can evaluate that exact expression
$ x="1.e8*1.07**100"
$ y=$(perl -E "say $x")
$ echo $y
86771632556.6417

Contents of ls appearing in middle of output

I am encountering a very weird situation when wrapping a bash script call in echo $(). This is strange enough that I don't know what code to present, so I will describe the general situation. I have a script, which we will call "run.sh", and it has some output. This is generally formatted quite nicely, with whitespace and line breaks.
I am trying to compare this output with a value that I got when I ran it once previously. To do this, the code compares the "new" value with the old by checking if these two are the same, i.e.:
expression=$(./runProcess.sh "$process");
expected=$(cat UnitTests/expect-process-$process);
if [ "$expression" == "$expected" ]; then
Clearly to get a value of "old" to compare with future testings I need to compute $(./runProcess.sh) by hand. When I do this, I get a version of the output with significantly less whitespace. However it is clearly wrong, because the contents of ls turn up in the middle of it. By that I mean that I get the following type of output running these two commands:
./runProcess.sh g,g:
R2With2Gluons =
+ ncol*i_*pi_^2*A*g^2 * (
- 17/24*d_(mu1,mu2)*d_(m1,m2)*p1.p1
- 31/8*d_(mu1,mu2)*d_(m1,m2)*p1.p2
- 17/24*d_(mu1,mu2)*d_(m1,m2)*p2.p2
+ 7/12*d_(m1,m2)*p1(mu1)*p1(mu2)
+ 1/24*d_(m1,m2)*p1(mu1)*p2(mu2)
+ 89/24*d_(m1,m2)*p1(mu2)*p2(mu1)
+ 7/12*d_(m1,m2)*p2(mu1)*p2(mu2)
);
0.01 sec out of 0.01 sec
echo $(./runProcess.sh g,g):
R2With3Gluons = + coeff(m1,m2,m3)*ncol*pi_^2*A*g^3 Auto Diagrams UnitTests colourCalc.frm form.set functions.frm output.frm process.frm process.mid qgraf2form.frm qgrafProcessor.py runProcess.sh runProcesses.sh test vertices.frm ( + 35/24*d_(mu1,mu2)*p1(mu3) - 35/24*d_(mu1,mu2)*p2(mu3) - 35/24*d_(mu1,mu3)*p1(mu2) + 35/24*d_(mu1,mu3)*p3(mu2) + 35/24*d_(mu2,mu3)*p2(mu1) - 35/24*d_(mu2,mu3)*p3(mu1) ); 0.40 sec out of 0.40 sec
And here is ls:
ls:
Auto form.set process.mid runProcesses.sh
Diagrams functions.frm qgraf2form.frm test
UnitTests output.frm qgrafProcessor.py vertices.frm
colourCalc.frm process.frm runProcess.sh
I can provide exact examples if necessary, but I hope this is illuminating enough. Why could this possibly be happening? I'm using bash on OS X Mountain Lion.
Use more quotes!!!
Try:
echo "$(./run.sh)"
instead. (Yes, with quotes).
Try:
old=$(./run.sh)
echo "$old"
you'll have the correct output (with $old in quotes). Now, regarding your test, use, as advised by sampson-chen:
[[ "$old" == "$(./run.sh)" ]]
(you don't need to quote the variables or the command substitution when assigning the variable old, but, as a general rule, you can use quotes every time). ((see Gordon Davisson's excellent comments to this post, that I've actually upvoted, with a few caveats about globs and quoting variables inside [[ ... ]])).
Edit. As you've edited your post, I see you're using an inefficient cat. Instead of:
expected=$(cat UnitTests/expect-process-$process)
please use
expected=$(< "UnitTests/expect-process-$process")
It's hard to say without your exact script, but for starters, your comparison:
old == $(./run.sh);
should be:
if [[ "$old" == "$(./run.sh)" ]]; then

Using multiple values for single variable in a loop

I have a small loop problem as below.
I have 3 different values to be used while running the same script -
va1="some-value1"
va2="some-value2"
va3="some-value3"
Now I want to use these three variable values to be used for running the same command, like -
while (i=0,i<=3,i++)
do
bin/java -s (run something with $var);
done
Now I want $var taking the value of var1, var2 and var3 each time it runs,
so can someone please tell me how do we achieve the above?
I tried doing -
for $1 $2 $3
do
case 1
case 2
case 3
done
OR
while read a b c
do
<code assumed to have loop iteration>
done <<< $(command)
But it isnt working as expected... Would really appreciate your help on this.
Thanks,
Brian
You forgot the 'in' part of the syntax:
for var in $va1 $va2 $va3
do
command $var
done
try
while ((i=0,i<=3,i++))
do
eval bin/java -s \$var$i
done
This is a great example of how to use eval. Note that 1. the value of $i is seen by the shell has it scans the line. Then because $var is escaped like \$var, it is not
'make visible' in the first scan. 2. Eval forces a 2nd scan of the cmd-line, so it sees $var1 or whatever, and that value is substituted into the command-line for execution.
I hope this helps.
P.S. Welcome to StackOverflow and let me remind you of three things we usually do here: 1) As you receive help, try to give it too, answering questions in your area of expertise 2) Read the FAQs, http://tinyurl.com/2vycnvr , 3) When you see good Q&A, vote them up by using the gray triangles, http://i.imgur.com/kygEP.png , as the credibility of the system is based on the reputation that users gain by sharing their knowledge. Also remember to accept the answer that better solves your problem, if any, by pressing the checkmark sign , http://i.imgur.com/uqJeW.png
You can use indirect variable expansion with ${!...}:
va1="some-value1"
va2="some-value2"
va3="some-value3"
for ((i=1;i<=3;i++)); do
varname="va$i"
echo "$varname = ${!varname}"
done
This prints:
va1 = some-value1
va2 = some-value2
va3 = some-value3

Resources