Simple Address book in Bash

John F. Moore

 
           
            Mail to: John F. Moore at WPCUG 
 
        

Last Revised: 2006-03-14 22:07

Revision History
Revision 1.0 Tue Mar 14 11:57:47 EST 2006

Abstract

This talk will discuss a database like structure for a single file. We will then see how to create a simple address book using only shell scripts and a file.


Table of Contents

1. What is a Database in general?
2. Shell Tool Phone Book
2.1. Design First
2.2. Lets build the modules
2.3. Lets tie this all together
3. Improved Shell Address Book
3.1. New Version of Phone Book

1.  What is a Database in general?

Before we start describing how to create an address book using shell scripts I thought we might start off by describing a database. Lets begin with a quote:

 

A database is an organized collection of data. ......

One possible definition is that a database is a collection of records stored in a computer in a systematic way, so that a computer program can consult it to answer questions. For better retrieval and sorting, each record is usually organized as a set of data elements (facts). The items retrieved in answer to queries become information that can be used to make decisions. ......

 
  -- Wikipedia Database Definition

So given this definition, how is a database different than a file of facts?

Essentially, they are the same. The biggest difference most people see is how the data is entered and retieved from the database.

I wanted to talk about databases as a point of reference. The idea that you need a special program like MySQL, or SQL Server, or DB2 or a special program is not true. You can build a database like program using nothing more than shell script.

So we will proceed to build an address book using shell scripts and a file. This address book used to be known as lesson 5.

2.  Shell Tool Phone Book

First lets state the goal: Create a shell program to store names and phone numbers. Now lets bullet the design ideas.

2.1.  Design First

  1. View contents of the phone book. Display the contents of the phone book preceeding each entry with a line number for easy reference.

  2. Add new entries to the phone book. Alway add the entries to the end of the book.

  3. Find specific entries in the book. Ask the user for a name or number to search for, then find that entry.

  4. Delete entries from the book. Ask he user which line to delete, then remove that line.

OK, now lets do some archetecture design.

The data will be stored in a text file in the following format.

Name ; phone number

Each entry will be on a separate line terminated by a line feed.

2.2.  Lets build the modules

I suggest we start with a script to add entries to the phone book. Here is one I cooked up.

        #!/bin/sh
        # File: /home/john/work/bayer/training/add.sh
        # Date: Wed Feb 27 18:06:30 EST 2002
        # Last Revised: Time-stamp: <2002-02-27 18:45:45 john> maintained by emacs
        # Description: Add entries to the address book
        # ======================================================================= 

        # Name of address book
        BOOK="address-book.txt"

        # Ask the user for a name and assign to a variable
        echo -n "Name of person: " 
        read name

        # Ask the user for a phone number and assign to a variable
        echo -n "Phone number: "
        read phone

        # Echo the answers and ask for confirmation
        echo "Should I enter the values:"
        echo -e "$name ; $phone \n"
        echo -n "y/n: "
        read answer

        if [ "$answer" == "y" ] 
        then
            # Write the values to the address book
            echo "$name ; $phone" >>$BOOK
        else
            # Give the user a message
            echo "$name ; $phone NOT written to $BOOK"
        fi

        exit 0
      

Now we have the ability to add entries to our book. Next we should build something to list the contents of our book.

        #!/bin/sh
        # File: /home/john/work/bayer/training/list.sh
        # Date: Wed Feb 27 18:04:46 EST 2002
        # Last Revised: Time-stamp: <2002-02-27 18:17:38 john> maintained by emacs
        # Description: List the entries in the address book with line numbers. 
        # ======================================================================= 

        BOOK="address-book.txt" 

        # Display the format before the entries
        echo "Line Number:   Name  ;  Phone Number"

        # Print the book with line numbers and paused with less
        nl --number-separator=":    " $BOOK | less 

      

Now suppose we want to search the address book to find a specific entry?

        #!/bin/sh
        # File: /home/john/work/bayer/training/find.sh
        # Date: Wed Feb 27 18:50:13 EST 2002
        # Last Revised: Time-stamp: <2002-02-27 18:53:00 john> maintained by emacs
        # Description: Find a specific line in the file.
        # ======================================================================= 

        BOOK="address-book.txt"

        # Ask the user what to look for.
        echo -n "What person or number are you seeking: "
        read find

        # Print the header before the answer
        echo "Name ; Phone number"
        grep -i $find $BOOK

      

Lastly in the tool set, build a tool to delete a specific entry in the phone book.

        #!/bin/sh
        # File: /home/john/work/bayer/training/del.sh
        # Date: Wed Feb 27 18:21:33 EST 2002
        # Last Revised: Time-stamp: <2002-02-27 18:47:49 john> maintained by emacs
        # Description: Delete the line specified by the user.
        # ======================================================================= 

        BOOK="address-book.txt"

        # Ask the user which line to delete
        echo -n "Which line should I delete: "
        read number

        # Rename the file before deleting
        mv $BOOK boo.txt

        # Add line numbers and delete against that number
        nl --number-separator=":" boo.txt | grep -v $number: | awk -F: '{print $2}' |  tee $BOOK

     

2.3.  Lets tie this all together

OK, now that we have the individual shell scripts to perform the functions, why not make a master script to call the individual scripts. Here is what I created in the class.

        #!/bin/sh
        # File: d:/home/john/tmp/phone.sh
        # Date: Thu 28 Feb 2002 10:35:24 Eastern Standard Time
        # Last Revised: Time-stamp: <2002-02-28 10:46:15 exmxm> maintained by emacs
        # Description: Master script for phone book program
        # ======================================================================= 

        # Name of address book
        BOOK="address-book.txt"

        exit=0

        while [ $exit -ne 1 ]
        do
            echo "What operation do you want?"
            echo -e "add, list, find, del, exit: "
            read answer

            if [ "$answer" = "add" ]
            then
                ./add.sh
            elif [ "$answer" = "list" ]
            then
                ./list.sh
            elif [ "$answer" = "find" ]
            then
                ./find.sh
            elif [ "$answer" = "del" ]
            then
                ./del.sh
            elif [ "$answer" = "exit" ]
            then
                exit=1
            else
                echo "I do not understand the command."
            fi
        done

        exit 0

      

3.  Improved Shell Address Book

OK, we are going to take the original shell scripts for the phone book program and expand them. Lets take a minute and define the improvements wanted.

  1. Add street, city, state, and zip code to records

  2. Display the records in a more common format.

  3. Add the ability to search only specified fields

3.1.  New Version of Phone Book

Ok here is a listing of the Design changes to be made. I will then show the code that was produced.

In the phone.sh file, which will be renamed to phone2.sh, clear the screen before printing the user prompt. This will give it a cleaner look. In addition, define the storage file here and not in the individual files.

        #!/bin/sh
        # File: /dosd/home/john/tmp/phone2.sh
        # Date: Wed Mar  6 08:17:29 EST 2002
        # Last Revised: Time-stamp: <2002-03-07 09:28:14 john> maintained by emacs
        # Description: Master script for phone book program
        # Revised for the second version of the phone book.
        # ======================================================================= 

        # Name of address book
        BOOK="address-book2.txt"

        export BOOK

        exit=0

        while [ $exit -ne 1 ]
        do
            clear
            echo "What operation do you want?"
            echo -e "add, list, find, del, exit: "
            read answer

            if [ "$answer" = "add" ]
            then
                ./add2.sh
            elif [ "$answer" = "list" ]
            then
                ./list2.sh
            elif [ "$answer" = "find" ]
            then
                ./find2.sh
            elif [ "$answer" = "del" ]
            then
                ./del2.sh
            elif [ "$answer" = "exit" ]
            then
                exit=1
            else
                echo "I do not understand the command."
                sleep 5
            fi
        done

        exit 0
      

In the add2.sh script, add additional requests for the additional data, street, city, state, and zip. Once the data is entered, display the results in a more normal format. Paying attention to the look of the input and output will make the program seem more professional. At the end of any output, besure to pause for the user to be able to read the output before clearing it.

        #!/bin/sh
        # File: /dosd/home/john/tmp/add2.sh
        # Date: Wed Mar  6 08:19:36 EST 2002
        # Last Revised: Time-stamp: <2002-03-06 09:03:48 john> maintained by emacs
        # Description: Add entries to the address book
        # Revisied: Modifyed for the second version of phone book
        # ======================================================================= 

        # echo "Book = $BOOK"
        echo -e "\n ======== Beginning Adding Addresses =======\n\n"

        # Ask the user for a name and assign to a variable
        echo -n "Name of person: " 
        read name

        # Ask for the street address
        echo -n "Street Address: "
        read street

        # Ask for the city
        echo -n "City: "
        read city

        # Ask for the state address
        echo -n "State: "
        read state

        # Ask for the zip address
        echo -n "Zip Code: "
        read zip

        # Ask the user for a phone number and assign to a variable
        echo -n "Phone number: "
        read phone

        # Echo the answers and ask for confirmation
        echo "Should I enter the values:"
        echo -e " $name \n $street \n $city, $state, $zip \n $phone \n"
        echo -n "y/n: "
        read answer

        # Convert the answer to lower case
        fixedanswer=`echo $answer | tr "A-Z" "a-z"`;

        if [ "$fixedanswer" = "y" ] 
        then
            # Write the values to the address book
            echo "$name;$street;$city;$state;$zip;$phone" >>$BOOK
            echo "Added the entry OK"
            sleep 5
        else
            # Give the user a message
            echo -e " $name \n $street \n $city, $state, $zip \n $phone \n NOT written to $BOOK"
            sleep 5
        fi

        exit 0

      

In the file list2.sh, provide a more normal display showing the output in a more natural format. Pass the user information on how to end the display and return to the top level program.

        #!/bin/sh
        # File: /home/john/work/bayer/training/list.sh
        # Date: Wed Feb 27 18:04:46 EST 2002
        # Last Revised: Time-stamp: <2002-03-06 09:07:04 john> maintained by emacs
        # Description: List the entries in the address book with line numbers. 
        # ======================================================================= 

        # Read each line in the book and display

        (awk -F ";" '{printf "Record: %d\n\t%s\n\t%s\n\t%s, %s, %s\n\t%s\n==========================\n\n", NR, $1, $2, $3, $4, $5, $6}' $BOOK ; echo "Press Q to Quit and return to the menu." ) | less

        # Display the format before the entries
        #echo "Line Number:   Name  ;  Phone Number"

        # Print the book with line numbers and paused with less
        #nl --number-separator=":    " $BOOK | less 

      

In the find2.sh script, allow the user to select which field to search, and what to search for. When the result is found, format the output before displaying.

        #!/bin/sh
        # File: /dosd/home/john/tmp/find2.sh
        # Date: Wed Mar  6 09:07:47 EST 2002
        # Last Revised: Time-stamp: <2002-03-07 09:30:03 john> maintained by emacs
        # Description: Find a specific line in the file.
        # Revised for a more complex search capability
        # ======================================================================= 

        # Ask the user what to look for.
        echo -n -e "What field would you like to search: \n\tname, street, city, state, zip, or phone: "
        read field

        echo -n "In the field = \"$field\", what string should I find? "
        read string 

        # Find the string in the selected field
        case $field in  

            "name" ) # Search for a specific name
                     awk -v var=$string -F ";" '$1 ~ var {printf "Record: %d\n\t%s\n\t%s\n\t%s, %s, %s\n\t%s\n", NR, $1, $2, $3, $4, $5, $6}' $BOOK | less
                     # result=`awk -F ";" '{printf "%d: %s\n", NR, $1}' $BOOK | grep -i $string | \
                             #awk -F ":" '{print $1}' `
                     ;;

            "street" ) # Search for a specific name
                       awk -v var=$string -F ";" '$2 ~ var {printf "Record: %d\n\t%s\n\t%s\n\t%s, %s, %s\n\t%s\n", NR, $1, $2, $3, $4, $5, $6}' $BOOK | less
                      ;;

            "city" ) # Search for a specific name
                     awk -v var=$string -F ";" '$3 ~ var {printf "Record: %d\n\t%s\n\t%s\n\t%s, %s, %s\n\t%s\n", NR, $1, $2, $3, $4, $5, $6}' $BOOK | less
                     ;;

            "state" ) # Search for a specific name
                      awk -v var=$string -F ";" '$4 ~ var {printf "Record: %d\n\t%s\n\t%s\n\t%s, %s, %s\n\t%s\n", NR, $1, $2, $3, $4, $5, $6}' $BOOK | less
                     ;;

            "zip" ) # Search for a specific name
                    awk -v var=$string -F ";" '$5 ~ var {printf "Record: %d\n\t%s\n\t%s\n\t%s, %s, %s\n\t%s\n", NR, $1, $2, $3, $4, $5, $6}' $BOOK | less
                     ;;

            "phone" ) # Search for a specific name
                      awk -v var=$string -F ";" '$6 ~ var {printf "Record: %d\n\t%s\n\t%s\n\t%s, %s, %s\n\t%s\n", NR, $1, $2, $3, $4, $5, $6}' $BOOK | less
                     ;;

            "*" ) # Search pattern not recognized
                  echo "I did not understand your field name";
                  ;;
        esac

        # echo "Result = $result";

        # if [ "$result" != "" ]
        #     then
        #     for number in $result
        #     do 
        #        awk -v val=$number  -F ";" 'NR == val {printf "Record: %d\n\t%s\n\t%s\n\t%s, %s, %s\n\t%s\n", NR, $1, $2, $3, $4, $5, $6}' $BOOK
        #     done
        # else
        #     echo "Did not find $string in $field"
        # fi

        # echo "Past Case"
        # sleep 5;

        exit 0

      

Finally in the del2.sh script, the only change is the remove the reference to the storage location.

        #!/bin/sh
        # File: /home/john/work/bayer/training/del2.sh
        # Date: Wed Feb 27 18:21:33 EST 2002
        # Last Revised: Time-stamp: <2002-03-07 18:15:04 john> maintained by emacs
        # Description: Delete the line specified by the user.
        # ======================================================================= 


        # Ask the user which line to delete
        echo -n "Which line should I delete: "
        read number

        # Display the record selected
        awk -v var=$number -F ";" 'NR ~ var {printf "Record: %d\n\t%s\n\t%s\n\t%s, %s, %s\n\t%s\n", NR, $1, $2, $3, $4, $5, $6}' $BOOK

        # Ask the user if this is correct before deleting.
        echo -n "Is this the correct record to delete? (y/n): "
        read answer

        # Lower case the answer and check
        fixedanswer=`echo $answer | tr "A-Z" "a-z"`;
        if [ "$fixedanswer" = "y" ] 
        then

            # Rename the file before deleting
            mv $BOOK boo.txt

            # Add line numbers and delete against that number
            nl --number-format=rz --number-separator=":" boo.txt | grep -v 0$number: | awk -F: '{print $2}' |  tee $BOOK

        else

            echo "Did not delete the record"
            sleep 5

        fi

      

The best way to gain experience with shell scripts is to play with them. Here is a tar archive of the shell scripts used in this talk. Enjoy.