Finding an average

Basically, I need to find average of numbers which are given like:
sh average file1 file (in files can be more than one number)
->10
sh average 5 7
->6
sh average /users/file
->5
echo 5 7 | sh average
6
So basically i wrote my code but it gives me error... I am pretty sure it has to work for first two cases at least.... But I stucked with my error messages ;(
this is my code:

sum=0
n=0
for i in $*
do
    if [ -f $i ]
    then
        while read line
        do

           n=` expr $n + 1 `
           sum=` expr $sum + $line `
                         done < $i

        else
           sum=` expr $sum + $i `
           n=` expr $n + 1 `
           fi


         done

          average=` expr $sum / $n `
          echo $average

Your requirement is not quite clear.
The following reads numbers of files from arguments, or from stdin if no arguments are provided.
The numbers in files or stdin can be placed in columns or rows or both

set -f # no wildcard globbing
sum=0
n=0

from_stdin() {
while read line
do
    set -- $line
    for i
    do
       sum=` expr $sum + $i `
       n=` expr $n + 1 `
    done
done
}

if [ -z "$1" ]
then
   from_stdin
else
    for i
    do
        if [ -f "$i" ]
        then
            from_stdin < "$i"
        else
            sum=` expr $sum + $i `
            n=` expr $n + 1 `
        fi
    done
fi

if [ $n -gt 0 ]
then
    average=` expr $sum / $n `
    echo $average
fi
1 Like
you have to use bc and you also need to check argument exist or not

try

#!/bin/ksh

check(){
    if [ -z "$1" ];then echo "No user input exiting..";exit;fi
       }

check $1

sum=0
n=0
for i in $*
do
    if [ -f $i ]
    then
        while read line
        do
           n=` expr $n + 1 `
           sum=` expr $sum + $line `
        done < $i

        else
           sum=` expr $sum + $i `
           n=` expr $n + 1 `
           fi
done
    average=$(echo "scale=3; $sum/$n" | bc -l )
    echo $average
1 Like

it is strange.....sometimes it gives me an error:
expr: syntax error
expr: syntax error
(standard_in) 1: parse error????
But sometimes it works fine O_o

My code is ok if file contains numbers in columns.... How to fix it in way that code works with files where numbers can be in rows as well, or both?
---------- Post updated at 08:29 PM ---------- Previous update was at 08:26 PM ----------

I tried with your changes but it gives me error like

expr: not a decimal number: 'doc1'
expr: syntax error??

So, did you invoke your script with doc1 as an operand (when there is no file named doc1), or did you specify a file as an operand where the file exists and contains the word doc1 instead of just numbers?

Thanks I found out what is wrong. But can you recommend smth with previous comment about how to modify code with file containing numbers in row also.....

Of course I could. Depending on what shell you're using and what operating system you're using, the script you have is probably not only not working in some cases, but also very inefficient in the cases that are working. So, to provide you with a reasonable, working solution, how about answering some questions first:

  1. What shell are you using?
  2. If you aren't using the Korn shell, is it OK to use the Korn shell instead of the shell you're using?
  3. Do you have access to a 1993 or later version of ksh?
  4. What operating system are you using?
  5. What is the name of your file that contains one or more rows with more than one number in a row?
  6. What is the actual contents of the file that contains more than one number in one or more rows?
  7. What happens when you use set -xvf instead of set -f to trace the commands your script is running (so you can actually see the expr command that is failing)?
  8. Show us some sample invocations of your script, the contents of any files named in those sample invocations, and the output that you expect from each of those sample invocations!

I m using Mac Pro..... So it gives you information what I am using i guess....
But as long as you recommend the strategy or what i have to add then you do not need to rewrite my code.

Basically, This is my piece of code..I just think that we can add smth for case with rows of numbers in file.
One more thing:
for example we have file1 and file2:
cat file1
-> 13 4 6 7 3
cat file2
-> 3
5
6

so when i run my code:
sh average file1 file2
->10 (WHICH is average of all numbers)
i can have many files with numbers in rows or columns or both. Space between numbers also can be double or triple etc. Hope u help me.
I thought also to add to while loop tr ' \n ' ' , ' and tr ' ' ' , ' :slight_smile:

#/bin/sh

check(){
    if [ -z "$1" ];
  then echo "No user input";
exit;fi
       }

check $1




sum=0
n=0 
for i in $*
do
echo $i
    if [ -f $i ]
    then
        while read line        
        do
  n=` expr $n + 1 `
           sum=` expr $sum + $line `
                         done < $i

        else
           sum=` expr $sum + $i `
         n=` expr $n + 1 `
           fi


         done

 average=$(echo "scale=3; $sum/$n" | bc -l )
        #  average=` expr $sum / $n `
          echo $average

For your above files,

echo scale=2";" "(" $(cat file{1..2}|tr -s ' \n' '+') "0)" / $(cat file{1..2}|wc -w) | bc
5.87

will do.

Thank u:) One more thing it is more relating to the input from pipes...for example
echo 5 3 | sh my_code. What I need to add to my code for accepting input from pipes? Would appreciate if you tell what I need to add in my piece of code...thanks

check(){
    if [ -z "$1" ];
  then echo "No user input";
exit;fi
       }

check $1
sum=0
n=0
t=0
for i in $*
do
          if [ -f $i ]
                  then
                  while read line
                        do
                       for j in $line
                                do

                           n=` expr $n + 1 `
                           sum=` expr $sum + $j `
                                        done
                                        done< $i
           else
                    sum=` expr $sum + $i `
                    n=` expr $n + 1 `
           fi

          done


         average=$(echo "scale=2; $sum/$n" | bc -l )
        # average=` expr $sum / $n `
         echo $average

You never did answer my question about what shell you're using and whether or not you had access to a 1993 or later version of the Korn shell. You've said you're using a Mac, so if you're using a recent version of OS X you do have a recent Korn shell. Translating your script to just use ksh built-ins (and adding a little structure to your indentation), the following Korn shell script seems to do what you want without all of the invocations of expr and bc:

#!/bin/ksh
n=0                                             # initialize number of numbers
sum=0                                           # initialize sum of numbers
if [ $# -gt 0 ]
then    for i in $*                             # Process command line args
        do      if [ -f $i ]
                then    while read line         # read from file named by arg
                        do      for j in $line  # for each number on the line
                                do      ((n++)) # increment count and update sum
                                        ((sum += j))
                                done
                        done < $i
                else    ((sum += i))            # process command line number
                        ((n++))
                fi
        done
else    while read line                         # Process numbers from stdin
        do      for j in $line
                do      ((n++))
                        ((sum += j))
                done
        done
fi
average=$(( (sum + 0.0) / n))                   # calculate average
printf "%.2f\n" $average                        # round to 2 decimal places

Obviously, there should be some verification that arguments that are not filenames on the command line and that data read from files (including standard input) is numeric data, but I'll leave that as an excercise for the reader. If there are no command line operands, this script will read data from standard intput. In this case, it does not care if the standard input is from the user typing at a terminal, redirected from a file, or a pipe.

The logic in this script follows the same logic used in your last script except that it reads from standard input instead of printing a diagnostic message if there are no operands given to your script.