<?xml version="1.0"?>
<!-- Declaration used on Merlin -->
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" 
"/usr/share/sgml/docbook/xml-dtd-4.4/docbookx.dtd"[
<!ENTITY timestamp SYSTEM "timestamp.txt">
]> <!--
# Last Revised: Time-stamp: <2006-03-14 22:07:09 john> maintained by emacs
# File: /home/john/work/1and1/lessons/database/address.xml
# Date: Tue Mar 14 12:00:19 EST 2006
# Description: Address book in shell scripts
# $Id: $
# ======================================================================= 
 -->

<article>
  <articleinfo>
    <title> Simple Address book in Bash </title>
    <abstract>
      <para> 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.
        </para>
      </abstract>

    <author> 
      <firstname> John </firstname>
      <surname> Moore </surname>
      <othername role='mi'>F.</othername>
      <affiliation>
	<address> <!-- email -->
           <ulink url="mailto:john.wpcug@lions-wing.net">
            Mail to: John F. Moore at WPCUG </ulink> 
        </address>
      </affiliation>
      </author>

    <copyright>
      <year> 2006 </year>
      <holder role="mailto:john.wpcug@lions-wing.net"> John F. Moore </holder>
    </copyright>

    <legalnotice> 
	<ulink
	  url="http://creativecommons.org/licenses/by-nc-sa/2.5/">
	  <inlinemediaobject>
	    <imageobject>
	      <imagedata
		fileref="http://creativecommons.org/images/public/somerights20.png"
		format="PNG" scale="100" align="center" />
	    </imageobject>
	    <textobject>
	      <phrase>Creative Commons License</phrase>
	    </textobject>
	  </inlinemediaobject>
	</ulink>
	<parameter>This work is licensed under a</parameter>
	<ulink
	url="http://creativecommons.org/licenses/by-nc-sa/2.5/">
	Creative Commons Attribution-NonCommercial-ShareAlike 2.5
	License </ulink>

    </legalnotice>

    <pubdate>Last Revised:  &timestamp; </pubdate>
    <revhistory>
      <revision>
	<revnumber> 1.0 </revnumber>
	<date> Tue Mar 14 11:57:47 EST 2006 </date>
      </revision>
    </revhistory>
  </articleinfo>

  <sect1 id="intro">
    <title> What is a Database in general? </title>  
    <mediaobject>
      <imageobject>
	<imagedata fileref="edu095.png" format="PNG" scale="40"/>
      </imageobject>
    </mediaobject>

    <para> 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: </para>

    <blockquote>
      <attribution> <ulink
      url="http://en.wikipedia.org/wiki/Database"> Wikipedia Database
      Definition </ulink></attribution>

      <para>A database is an organized collection of
      data. ...... </para> 

      <para> 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. ......</para> 
    </blockquote>

    <para> So given this definition, how is a database different than
    a file of facts?  </para>

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

    <para> 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.</para>

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

  </sect1>

  <sect1>
    <title> Shell Tool Phone Book </title>
    <mediaobject>
      <imageobject>
	<imagedata fileref="hom173.png" format="PNG" scale="40"/>
      </imageobject>
    </mediaobject>

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

    <sect2>
      <title> Design First </title>

      <orderedlist>
	<listitem>
	  <para> <emphasis> View contents of the phone book.</emphasis>
          Display the contents of the phone book preceeding each entry
          with a line number for easy reference.</para> 
	</listitem>

	<listitem>
	  <para> <emphasis> Add new entries to the phone
	  book.</emphasis> Alway add the entries to the end of the book.</para>
	</listitem>

	<listitem>
	  <para> <emphasis>Find specific entries in the
	  book. </emphasis> Ask the user for a name or number to search
	  for, then find that entry. </para>
	</listitem>

	<listitem>
	  <para> <emphasis>Delete entries from the book.</emphasis>
	  Ask he user which line to delete, then remove that line. </para>
	</listitem>

      </orderedlist>

      <para> OK, now lets do some archetecture design. </para>

      <para>The data will be stored in a text file in the following
    format. </para>

      <para> <computeroutput> Name ; phone number </computeroutput></para>

      <para> Each entry will be on a separate line terminated by a line
    feed. </para>
    </sect2>

    <sect2>
      <title> Lets build the modules </title>

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

      <programlisting>
        #!/bin/sh
        # File: /home/john/work/bayer/training/add.sh
        # Date: Wed Feb 27 18:06:30 EST 2002
        # Last Revised: Time-stamp: &lt;2002-02-27 18:45:45 john&gt; 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" &gt;&gt;$BOOK
        else
            # Give the user a message
            echo "$name ; $phone NOT written to $BOOK"
        fi

        exit 0
      </programlisting>

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

      <programlisting>
        #!/bin/sh
        # File: /home/john/work/bayer/training/list.sh
        # Date: Wed Feb 27 18:04:46 EST 2002
        # Last Revised: Time-stamp: &lt;2002-02-27 18:17:38 john&gt; 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 

      </programlisting>

      <para> Now suppose we want to search the address book to find a
      specific entry? </para>

      <programlisting>
        #!/bin/sh
        # File: /home/john/work/bayer/training/find.sh
        # Date: Wed Feb 27 18:50:13 EST 2002
        # Last Revised: Time-stamp: &lt;2002-02-27 18:53:00 john&gt; 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

      </programlisting>

      <para> Lastly in the tool set, build a tool to delete a specific
      entry in the phone book. </para>

      <programlisting>
        #!/bin/sh
        # File: /home/john/work/bayer/training/del.sh
        # Date: Wed Feb 27 18:21:33 EST 2002
        # Last Revised: Time-stamp: &lt;2002-02-27 18:47:49 john&gt; 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

     </programlisting>

    </sect2>

    <sect2>
      <title> Lets tie this all together </title>

      <para> 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. </para>

      <programlisting>
        #!/bin/sh
        # File: d:/home/john/tmp/phone.sh
        # Date: Thu 28 Feb 2002 10:35:24 Eastern Standard Time
        # Last Revised: Time-stamp: &lt;2002-02-28 10:46:15 exmxm&gt; 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

      </programlisting>

    </sect2>

  </sect1>

  <sect1>
    <title> Improved Shell Address Book </title>
    <mediaobject>
      <imageobject>
	<imagedata fileref="home39.png" format="PNG" scale="40"/>
      </imageobject>
    </mediaobject>

    <para> 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. </para>

    <orderedlist>
      <listitem>
	<para> Add street, city, state, and zip code to records </para>
      </listitem>

      <listitem>
	<para> Display the records in a more common format. </para>
      </listitem>

      <listitem>
	<para> Add the ability to search only specified fields </para>
      </listitem>
    </orderedlist>

    <sect2>
      <title> New Version of Phone Book </title>

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

      <para> 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.
      </para>

      <programlisting>
	#!/bin/sh
	# File: /dosd/home/john/tmp/phone2.sh
	# Date: Wed Mar  6 08:17:29 EST 2002
	# Last Revised: Time-stamp: &lt;2002-03-07 09:28:14 john&gt; 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
      </programlisting>

      <para> 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.
      </para>

      <programlisting>
	#!/bin/sh
	# File: /dosd/home/john/tmp/add2.sh
	# Date: Wed Mar  6 08:19:36 EST 2002
	# Last Revised: Time-stamp: &lt;2002-03-06 09:03:48 john&gt; 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" &gt;&gt;$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

      </programlisting>

      <para> 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.
      </para>

      <programlisting>
	#!/bin/sh
	# File: /home/john/work/bayer/training/list.sh
	# Date: Wed Feb 27 18:04:46 EST 2002
	# Last Revised: Time-stamp: &lt;2002-03-06 09:07:04 john&gt; 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 

      </programlisting>

      <para> 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.
      </para>

      <programlisting>
	#!/bin/sh
	# File: /dosd/home/john/tmp/find2.sh
	# Date: Wed Mar  6 09:07:47 EST 2002
	# Last Revised: Time-stamp: &lt;2002-03-07 09:30:03 john&gt; 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

      </programlisting>

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

      <programlisting>
	#!/bin/sh
	# File: /home/john/work/bayer/training/del2.sh
	# Date: Wed Feb 27 18:21:33 EST 2002
	# Last Revised: Time-stamp: &lt;2002-03-07 18:15:04 john&gt; 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

      </programlisting>


    </sect2>

    <para> The best way to gain experience with shell scripts is to
    play with them.  Here is a <ulink url="phone-src.tar.gz"> tar
    archive </ulink> of the shell scripts used in this talk.  Enjoy. </para>

  </sect1>

</article>