2

I need an idea on how to complete this research project:

Write a Bash script to do a binary search. Read student names and grades from a file into an array.
Prompt the user for a student name. Find the name in the array and display the grade. If the student does not exist, print a message stating that.

The data in the file is below:

Ann:A
Bob:C
Cindy:B
Dean:F
Emily:A
Frank:C
Ginger:D
Hal:B
Ivy:A
Justin:F
Karen:D
Andreas Louv
  • 46,145
  • 13
  • 104
  • 123
  • `read -r my_var` can read from stdin or tty: `read -r my_var < /dev/tty` – Andreas Louv Jun 09 '16 at 19:09
  • I have not tried anything as yet. I am totally green to shell scripting. I will try to read up some things between today and tomorrow. Any support will be helpful. – Roberto Kippins Jun 09 '16 at 19:13
  • @RobertoKippins Get comfortable with the man pages, for instance: `man 1 read` . The `1` can usually be omitted but ensures you will find executables. For more info see `man man` :-). Another interesting read is: http://mywiki.wooledge.org/Quotes and http://mywiki.wooledge.org/BashPitfalls Don't get scared about it, but rater use it as a reference whenever you need to. Also shellcheck is a good tool and website to linting shell scripts – Andreas Louv Jun 09 '16 at 19:19
  • With current GNU bash: `declare -A data; while IFS=":" read name grade; do data["$name"]="$grade"; done < file; search="Bob"; echo ${data["$search"]}` – Cyrus Jun 09 '16 at 19:41

1 Answers1

1

Just a note about the binary search approach. Using greater-than / less-than to check whether the user's provided name is below or above the current name in the array works because the names are conveniently in alphabetical order (meaning that the person who gave you this assignment wanted you take advantage of this convenience).

The Code:

# Store the names and grades into arrays
names=( $( cut -d: -f1 filename ) )
grades=( $( cut -d: -f2 filename ) )

# Prompt user for a name
echo "Type the student's name (use proper capitalization!), followed by [ENTER]:"

# Read the user's response
read name

length=${#names[@]}
start=0
end=$((length - 1))
match=0
while [[ $start -le $end ]]; do
     middle_i=$((start + ((end - start)/2)))
     middle_item=${names[$middle_i]}
     if [[  $middle_item -gt $name ]]; then
         end=$((end - middle_i-1))
     elif [[ $middle_item -lt $name ]]; then
         start=$((middle_i+1))
     else
         # A match was found
         match=1
         echo "${name}'s grade is a(n): ${grades[$middle_i]}."
         break
     fi
done

# Check if a match was found
if [[ $match = 0 ]]; then
    echo "Couldn't find that student..."
fi

If you don't find the binary search to be a very readable approach, as I do, then I'd recommend using a for-loop:

# Find the user's provided name, print the 
for i in "${!names[@]}"; do
    # Check if the current name in the array is the same as the provided name
    if [[ "${names[$i]}" = "${name}" ]]; then
        # A match was found
        match=1
        echo "${name}'s grade is a(n): ${grades[$i]}."
        break
    fi
done

For the cut command, if you are not familiar...

-d: specifies the delimiter used in your given text file.

-f: specifies which field to keep (and thus store into the array).

Explaining the Code:

  1. You can think of the text file you have as a CSV file, except for : is used instead of , as a delimiter. For this reason, I use -d:. There are two columns, (i.e., two fields) per line in your text file.
  2. A field is essentially equivalent to a column in a CSV file. The fields are separated by the delimiter, :. The first field contains the student's name, so I used -f1 to capture the students' names. The second field contains the student's grade, so I used -f2 to capture the students' grades.

Done! Happy coding

Vladislav Martin
  • 1,504
  • 2
  • 15
  • 34
  • This worked well Martin, and thanks for all the details and explanations. From what I see there are many ways to achieve this and I will definitely study all comments and try to do it on my own. – Roberto Kippins Jun 09 '16 at 20:34
  • There are definitely quite a few ways of doing this in bash. If you plan of answering this question on your own and are no longer looking for help from StackOverflow, you should do something to indicate that you're not looking for an answer from the StackOverflow community. I hope this answer helps :) – Vladislav Martin Jun 09 '16 at 20:38
  • Also, by the way (just so you know) this question has been asked on this site already. See [this question](http://stackoverflow.com/q/22056855/5209610) if you want some more possible approaches. In my answer, I technically don't provide a *binary* search approach, though my answer should definitely give you a good start. – Vladislav Martin Jun 09 '16 at 20:56
  • I saw the question afterwards but none of the answers given were as clear as your with explanations. This was helpful since i am new to scripting.Thanks – Roberto Kippins Jun 10 '16 at 12:53
  • I see! Well, in that case, if you found myself helpful in answering your question, then please consider accepting it. – Vladislav Martin Jun 10 '16 at 13:28