views:

342

answers:

7

Can anyone tell me what I'm doing wrong here?

#!/bin/sh

if [ $# = 0 ]
then
    echo "Usage: $0 <filename>"
    exit 1
fi

sum=0
count=0

while [ $0 != 0 ]
do
        sum="$sum"+"$2"
        count="$count"+ 1

done
if [ "$count" != 0 ]
then
        avg="$sum"/"$count"
        printf "Sum= $sum \n Count= $count  \n Avg= $avg"
        exit 0
else
        printf "Sum= $sum \n Count= $count  \n Avg= undefined"
        exit 0
fi
exit 1

Here's the output when I try to test the code:

./average

sum: =: No such file or directory
sum: 0: No such file or directory
./average: 11: count: not found
[: 18: !=0: unexpected operator
./average: 25: Syntax error: Unterminated quoted string

Basically if I had a file that looked something like this:

FirstPerson 23 

SecondPerson 36

ThirdPerson 22

I want to be able to read that into my program and have it output:

Sum = FirstPerson+SecondPerson+ThirdPerson

Count = NumberofPeople

Average = Sum/Count
+1  A: 

To begin with, you shouldn't have spaces on either side of an =

pavium
Wow. I guess you can tell that I don't normally write shell scripts...haha. Thanks man.
에이바
+1  A: 

The error "Unterminated quoted string" is self explanatory

printf "Sum= \"$sum\" \n Count= \"$count\"  \n Avg= "\$avg\""

Should be

printf "Sum= \"$sum\" \n Count= \"$count\"  \n Avg= \"$avg\""
Anand
A: 

By looking at the script there does not seem to be much that you are doing correctly. I recommend looking at some Bash how to and follow simple steps to get it to do what you expect.

  1. no spaces after variable assignment, should be sum= and so on
  2. while [ ! -f $1 ] might actually do something but not what you expect
  3. read -p "Re-enter the filename and hit <Enter>: " definitely does not do what you expect
  4. and so on
stefanB
+5  A: 
 awk '{sum+=$2}END{printf "Sum=%d\nCount=%d\nAve=%.2f\n",sum,NR,sum/NR}' ave.txt

First off, Bash cannot do integer division, you will either need to pipe the math to a tool like 'bc' or just use awk to do it all as it's quite powerful; after all, that entire script of yours was turned into a 1-liner.

Sample Input

$ cat ave.txt
FirstPerson 23
SecondPerson 36
ThirdPerson 22

Result

Sum=81
Count=3
Ave=27.00
SiegeX
You beat me by a second :)
Arrieta
... and wrote a much better script. I will remove mine, the unworthy AWK contender.
Arrieta
+2  A: 

I don't know about your shell script, but I know you should be using the right tool for the job. That tool is AWK. It was designed specifically for this task and, if you are using UNIX (or Linux, or Mac OS X or whatever) you have it installed. This is the one-liner:

awk '{ sum+=$2; count+=1 } END {print "Sum =",sum; print "Count =",count; print "Average= ",sum/count}' test2.dat

Read the guide for AWK. The philosophy of UNIX is DO NOT REINVENT THE WHEEL. Use the right tools, buddy.

Good luck,

Arrieta
in my example I wrote a file called test2.dat with the data you provided.
Arrieta
Haha, so I did. Almost identical except for the fact that awk already provides you a count with 'NR'.
SiegeX
A: 

the code below works, you can probably optimize it if you want (or use awk, perl, etc):

#!/bin/bash

if [ $# -ne 1 ]; then
        echo "Usage: \"$0\" <filename>"
        exit
fi

if [ ! -f $1 ]; then
        echo "$1 file not found."
        echo "Usage: $0 <filename>"
        exit
fi

sum=0
count=0
arq=$1

while read line
do
        num=`echo ${line#* }`
        sum=`expr $sum + $num`
        count=`expr $count + 1`
done < "$arq"

if [ "$count" != 0 ]
then
        avg=`expr $sum / $count`
        printf "Sum= \"$sum\" \n Count= \"$count\"  \n Avg= \"$avg\""
        exit 0
else
        printf "Sum= \"$sum\" \n Count= \"$count\"  \n Avg= undefined"
        exit 0
fi
Peter Carrero
Although this is the accepted answer, it is *NOT* correct because 'expr' does not do floating point division. The calculated average in this code will only be correct if $count is an integer multiple of $sum. Try `expr 5 / 2` and you'll see the answer is 2, and not the correct value of '2.5'
SiegeX
siegex, i think you are hastily assuming that a the person asking the question wants a floating point answer... that may or may not be the case since he didn't specify it in his question.
Peter Carrero
I would contend to you that an Average that has been truncated to an integer is not at Average at all, but rather a Modulus.
SiegeX
@Peter: An arithmetic average has to be done in the reals, not integers (i.e., no integer division). Otherwise, as SiegeX points out, it is not an arithmetic average.
Arrieta
+2  A: 

try this

count_ppl=0
sum=0
while read a b
do
   sum=$((sum+b))
   count_ppl=$((count_ppl+1))
done < file
echo "Sum=$sum"
echo "Count=$count_ppl"
avg=$(echo "scale=2;$sum/$count_ppl" | bc)
echo "Average=" $avg
Good bash solution, although it can be optimized a bit more by using `((sum+=b))` and `((count_ppl++))` instead of the two lines you have above.
SiegeX
optimized? don't understand. any references or proof that says += or ++ ends up faster? if that's what you mean.
Optimized in terms of typing and code readability IMHO. I wouldn't think there would much of a difference, it at all, between the two efficiency wise.
SiegeX
then that doesn't matter. Its all personal preferences. Just a qns, does other shell support += assignment operator?