Blued00d
Blued00d

Reputation: 170

Bash Script Awk if statements

Given a input file with record format... (ID #, First name, Last name, Score1, Score2, Score3, Score(N).. Important note: There will be numerous records in file (not just 1 row).

I would like to be able to provide output format such as.. (ID #, Average Score, Grade letter corresponding to average score)

Example of input:

900123123 Joe Brown 100 90 80
900900900 Tyler Black 80 95 75
900231231 Stephen Williams 70 75 80
900111111 Andrew Smith 85 75 90

Example of output:

900123123 90 A
900900900 83.3 B
900231231 75 C
900111111 83.3 B

My issue is the if-statements to determine what grade letter to assign. Here is my current code:

#!/bin/bash
awk '
BEGIN {FS=OFS=" "}
{
sum=0; average=0
for(i=3;i<=NF;i++)
        {sum+=$i;}
average = (sum/(NF-3))
if (average -ge 90)
        print $1, $2, $3, average, " A";
else if(average -ge 80 && -le 90)
        print $1, $2, $3, average, " B";
else if(average -ge 70 && -le 80)
        print $1, $2, $3, average, " C";
else if(average -ge 60 && -le 70)
        print $1, $2, $3, average, " D";
else
        print $1, $2, $3, average, "F";
}' grades.txt

This will result in the output:

900123123 Joe Brown 90  A
900323323 Tyler Black 83.3333  A
900231231 Stephen Williams 75  A
900232232 Andrew Smith 83.3333  A
   0  A

Why is the first if statement being hit every time even when average is less than 90? We know this because it is used inside of the print statement and prints out A with any number. Also, I have no idea why 0 A is output and what the cause of this may be.

Upvotes: 2

Views: 4680

Answers (3)

Ed Morton
Ed Morton

Reputation: 203219

Your first condition was:

if (average -ge 90)

As @thatotherguy poined out, -ge is a shell construct and the equivalent in awk is >=. To awk your statement reads as:

if ( (average - ge) 90 )

which is to say: subtract the numeric value of the unassigned variable named ge (i.e. zero) from the variable average and then concatenate the string 90 to the result so if average was 70, for example then it'd read as:

if ( (70 - 0) 90 )

which after the subtraction is:

if ( 70 90 )

which after the concatenation is:

if ( 7090 )

which is a true condition and so you're script will always execute the subsequent line of code since no matter what the result of the subtraction is you're always going to concanetate it with 90 and end up with a non-zero and non-null result.

The 0 A at the end of your output is almost certainly because you have a blank line at the end of your input file. You can guard against that by testing NF before entering the action block.

Here's a suggestion on how to write your script:

awk 'NF {
    sum=0
    for(i=3;i<=NF;i++)
        sum+=$i
    average = sum/(NF-3)
    if      (average >= 90) grade = "A"
    else if (average >= 80) grade = "B"
    else if (average >= 70) grade = "C"
    else if (average >= 60) grade = "D"
    else                    grade = "F"
    print $1, $2, $3, average, grade
}' grades.txt

There's no point testing for && average < 90 when you're in the else part of a condition that tested for average >= 90.

Upvotes: 4

anubhava
anubhava

Reputation: 784968

This rafactored awk script should work for you:

#!/bin/bash
awk '{
sum=0;
for(i=3;i<=NF;i++)
        {sum+=$i;}
average = (sum/(NF-3));
avgstr = sprintf("%s%s%s%s%s%s%.2f", $1, OFS, $2, OFS, $3, OFS, average);
if (average >= 90)
        print avgstr, "A";
else if(average >= 80 && average <= 90)
        print avgstr, "B";
else if(average >= 70 && average <= 80)
        print avgstr, "C";
else if(average >= 60 && average <= 70)
        print avgstr, "D";
else
        print avgstr, "F";
}' grades.txt

OUTPUT:

900123123 Joe Brown 90.00 A
900900900 Tyler Black 83.33 B
900231231 Stephen Williams 75.00 C
900111111 Andrew Smith 83.33 B

Upvotes: 2

that other guy
that other guy

Reputation: 123410

There are two problems:

  1. -ge/-le is for bash, while awk is a separate programming language that uses >= and <=
  2. You can't shorten if statements by leaving out an operand the second time it's used (&& -le 90).

If you rewrite

if(average -ge 80 && -le 90)

into

if (average >= 80 && average <= 90)

it'll work.

Upvotes: 3

Related Questions