0

Just what it says. I'm trying to write a simple bash script from a question in a book. I'm trying to use the case statement, and for all I can tell I am using it correctly. However I keep getting "syntax error near unexpected token '$'in\r' case $i in.

No idea why. I'm also sure there are other problems in my code since this is my first script. The case statement is on line 10. Feel free to correct anything else, the end program will be building a grade data file for student grades, using a simple awk script to compute the average of the grades, and putting everything in the output file.

function getStudentData () { 
    i=0
    while [ $i<5 ]
    do
        case $i in
           [0]) echo -n "Enter student name\n"
              read name
              ;;
           [1]) echo -n "Enter Quiz grade\n" 
                read quiz
                checkLimits $quiz
                ;;
           [2]) echo -n "Enter homework grade\n"
                read hw
                checkLimits $hw
                ;;
            [3]) echo -n "Enter midterm grade\n"
                 read midterm
                 checkLimits $midterm
                  ;;
           [4]) echo -n "Enter Final grade \n"
                read final
                checkLimits $final
                ;;
        esac
done
}

function checkLimits ($grade) {
if [ grade <= 100 || grade >= 0 ]; then
    $i--
fi
}

if [ $# -lt 2 ]; then
echo "Incorrect number of arguments"
exit 1
fi

#Check awk existance
if [ ! -e $2 ]; then
echo "Error, .awk file does not exist"
exit 1
fi

#flag for data file existing, and awk file
flag=0

#Check for data file existing
if [ ! -e $1 ];then
flag=0
else
flag=1
fi
ans="yes"
while [ $ans == "yes" || $ans == "y" ]
do
echo "Do you want to enter a student record?"
read ans

if [ $ans == "y" || $ans == "yes" ];then
    getStudentData 

else
    echo "we done"
    exit 1
fi
done
user2079828
  • 645
  • 1
  • 8
  • 31
  • `[ $i<5 ]` really doesn't do what you think it does. – Kevin Oct 29 '13 at 03:02
  • does it need to be while (( $i < 5 )) ? – user2079828 Oct 29 '13 at 03:05
  • That will work, yes, but you need to understand why. Try checking the man pages of `[` and `bash`. – Kevin Oct 29 '13 at 03:07
  • Actually, that just creates an unexpected token at line 6: while (( $i < 5 )) I suspect the answer has to do with expressions in the while loop – user2079828 Oct 29 '13 at 03:08
  • Are you sure you're running this with `bash` and not, for example, `/bin/sh` ? – rici Oct 29 '13 at 03:09
  • first line of my script is: #!/bin/bash – user2079828 Oct 29 '13 at 03:11
  • I have named the file name.sh , and I'm currently test running it with the: sh name.sh command – user2079828 Oct 29 '13 at 03:12
  • `sh name.sh` will run it with `sh`, not `bash`, regardless of the shebang (first line). Either way, I've tried it out in both `bash` and `sh`, and `i=0; while (( i++ < 5 )); do echo $i; done` works fine. – Kevin Oct 29 '13 at 03:18
  • I dunno. I have changed it to : i=0 while (( i++ < 5 )) do Still doesn't work. Apparently the computer i'm using links the sh command to bash. – user2079828 Oct 29 '13 at 03:25
  • Yes, but `/bin/bash` looks to see whether it was invoked as `bash` or `sh` and behaves differently depending on which name was used. However, there are enough syntactic oddities in the code that the use of `bash` vs `sh` didn't make much difference. However, I've covered the issues I remember fixing in my answer. The script runs with `sh` that is linked to `bash`; it doesn't behave as well because of the `echo -n` notation, but does otherwise produce the correct output. – Jonathan Leffler Oct 29 '13 at 03:37
  • Why are you using a case statement inside a loop like this in the first place? Get rid of both: `function studentData () { echo; read; echo; read; checkLimits; echo; read; … }`. – chepner Oct 29 '13 at 12:32
  • I don't understand your question. The end program may not start at the first case, and I need to loop through the cases at whichever case I start at. Also, there will be a caveat will make allow the user to edit grades and skip grades they do not wish to enter. – user2079828 Oct 31 '13 at 02:36

1 Answers1

1

If you write it as a loop, then you should probably write:

function getStudentData () { 
    i=0
    while [[ $i < 5 ]]
    do
        case $i in
           0) echo -n "Enter student name: "
              read name
              ;;
           1) echo -n "Enter Quiz grade: " 
              read quiz
              checkLimits $quiz
              ;;
           2) echo -n "Enter homework grade: "
              read hw
              checkLimits $hw
              ;;
           3) echo -n "Enter midterm grade: "
              read midterm
              checkLimits $midterm
              ;;
           4) echo -n "Enter Final grade: "
              read final
              checkLimits $final
              ;;
        esac
    done
}

function checkLimits() {
    if [[ $1 -gt 100 || $1 -lt 0 ]]
    then ((i--))
    fi
}

This fixes a number of problems:

  1. The test condition in the while loop.
  2. The non-obvious use of echo -n to suppress a newline plus the \n to add a newline.
  3. The indentation.
  4. The syntax of the checkLimits() function.
  5. The syntax of the condition in the checkLimits() function.
  6. Uninverts the inverted logic of the condition in the checkLimits() function.
  7. Actually decrements the variable i.

It does not fix the reliance on the global variable $i, which is pretty ugly.

The purpose of the loop must be to go back if there's an error and get the data reentered. That's a clever idea. Unfortunately, you never increment $i, so it doesn't really work.

Here is some working code:

function getStudentData () { 
    for ((i = 0; i < 5; i++))
    do
        case $i in
           0) echo -n "Enter student name: "
              read name
              ;;
           1) echo -n "Enter Quiz grade: " 
              read quiz
              checkLimits $quiz
              ;;
           2) echo -n "Enter homework grade: "
              read hw
              checkLimits $hw
              ;;
           3) echo -n "Enter midterm grade: "
              read midterm
              checkLimits $midterm
              ;;
           4) echo -n "Enter Final grade: "
              read final
              checkLimits $final
              ;;
        esac
    done
}

function checkLimits() {
    if [[ $1 -gt 100 || $1 -lt 0 ]]
    then ((i--))
    fi
}

getStudentData
echo "Name     = $name"
echo "Quiz     = $quiz"
echo "Homework = $hw"
echo "Midterm  = $midterm"
echo "Final    = $final"

The output from a sample run:

Enter student name: Eileen
Enter Quiz grade: 92
Enter homework grade: 94
Enter midterm grade: 95
Enter Final grade: 97
Name     = Eileen
Quiz     = 92
Homework = 94
Midterm  = 95
Final    = 97
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • I completely agree with what you said, and I am aware I'm not incrementing anything yet. There must be something wrong with my other code. I deleted all my code, and put this code into a .sh file, then ran it with sh name.sh. At line 3: for (( i = 0; i < 5; i++ )) receives a syntax error near unexpected token '$'\r'' – user2079828 Oct 29 '13 at 03:40
  • You've got DOS line endings; it is complaining about the CR in the CRLF line endings. If you want DOS, use a Windows machine. If you want `bash`, use a Unix machine and make sure you use Unix line endings. See also: [How to convert DOS/Windows newline to Unix newline in bash script?](http://stackoverflow.com/questions/2613800/how-to-convert-dos-windows-newline-to-unix-newline-in-bash-script/) – Jonathan Leffler Oct 29 '13 at 03:45
  • Ah, ya I'm learning from windows environment and running on remote system. So basically all my lines end with \n when they need to end with \r? Or maybe I'm misunderstanding. I wrote an awk script a week or two ago and did not have this problem. – user2079828 Oct 29 '13 at 03:51
  • No; your lines end `\r\n` and need to end `\n`. Find the `dos2unix` or `dtou` command, or get nifty with `tr` or use `vim` and `:set fileformat=unix`. – Jonathan Leffler Oct 29 '13 at 03:52
  • Wonderful, I wondered why it kept referring to $\r when I had nothing resembling that. So I guess it interpreted the carriage return wrongly and only needs a newline. I will have to remember that. Thank you for your help, I've now fixed it. – user2079828 Oct 29 '13 at 04:06