ANU CECS comp2100 2nd case study for shell programming

A Second Case Study for Shell Programming

Summary

The Problem: Step 1

The CS department at ANU runs a cake club. The club has a manager whose responsibility is to notify every club member about every fortnightly meeting via email, and establish whose turn it is to bring the cake.

The member list is maintained in the file members.txt which has the following CSV format: a number of lines, each in the format

email@address,full name[,status]

This is an example of such file:

u1234456@anu.edu.au,Alexei B Khorev
rms@fsf.org,Richard M Stallman,HonMem
u2345678@anu.edu.au,Ian Barnes
torvalds@transmeta.com,Linus B. Torvalds,HonMem
chris.johnson@anu.edu.au,Chris Johnson

Theer are two categories: honorary members (marked by HonMem in the 3rd field) and regular members (with no 3rd field). Only regular members have to provide a cake for every meeting. The turns for cake duty are regulated by the roster.txt file, which lists all regular members in order of duty. The format of the roster.txt file is the same as the 2 fields of the members.txt. The initial roster.txt can be easily generated from the members.txt file by running the command:

shell-prompt> grep -v HonMem members.txt >roster.txt
with the result (numbers then added for clarity with the -n switch on cat).
shell-prompt> cat -n roster.txt
     1  u1234456@anu.edu.au,Alexei B Khorev
     2  u2345678@anu.edu.au,Ian Barnes
     3  chris.johnson@anu.edu.au,Chris Johnson

The first member on the list is the person who has to bring cake for the next club meeting.

We rely on the simplifying constraint that none of the email addresses, nor any part of the full name contain the pattern HonMem. We could be smarter and not need to reply on this—an exercise for the reader (awk can do a pattern match selection on individual fields.)

The task is to write a bash script called cake to

  1. read the roster.txt file and print out the name of the person whose turn it is to bring cake
  2. read the members.txt file, and send an "email" to every member about the forthcoming meeting. For the person whose turn it is to bring the cake, this message will also contain a reminder about the roster duty.

    Write the script in debugging mode: that is, output (echo) the list of email commands, rather than actually executing them.

The command cake will be run as follows:

shell-prompt> ./cake < members.txt

The output will look something like this:

	Hello, Alexei!
	Remind you that the cake club is meeting today at 10.30 am.
	This time it is your turn to bring the cake!
	See you there.
	The Cake club manager
	(sending to u1234456@anu.edu.au)

	Hello, Ian!
	Remind you that the cake club is meeting today at 10.30 am.
	See you there.
	The Cake club manager
	(sending to u2345678@anu.edu.au)

	   .... .... ....
	   .... .... ....
	   .... .... ....
	
	The new roster is
	    1  u2345678@anu.edu.au,Ian Barnes
	    2  chris.johnson@anu.edu.au,Chris Johnson
	    3  u1234456@anu.edu.au,Alexei B Khorev

Solution to Step 1 (discussed orally)

Result

	#!/bin/bash
	#who is the caker man today
	caker=$(head -1 roster.txt | cut -d',' -f2)
	caker_email=$(head -1 roster.txt | cut -d',' -f1)
	echo The roster man today is $caker
	echo His email address is $caker_email
	echo
	#looping through members.txt list and sending emails
	while read line
	do
	  email=$(echo $line | cut -d',' -f1)
	  fn=$(echo $line | cut -d',' -f2 | cut -f1)
	  echo Hello, ${fn}!
	  echo Remind you that the cake club is meeting today at 10.30 am.
	  if [ $caker_email == $email ]
	  then
	    echo This time it\'s your turn to bring the cake!
	  fi
	  echo See you there.
	  echo The Cake club manager, `whoami`

	  #to simulate sending the email
	  echo "(sending to $email)"
	  echo
	done
	#update the roster
	# use tail with the '+' option to only pick
        #all lines but the last one in the file
	tail +2 roster.txt > tmp.txt
	head -1 roster.txt >> tmp.txt
	mv tmp.txt roster.txt
	echo The new roster is
	cat -n roster.txt

Step 2: Making the script more versatile

Solution to Step 2 (discussed orally)

Result

shell-prompt>  cat addmember

#!/bin/bash
#check if there are CLA for adding new member
if [ $# -eq 2 ]
then
  if grep $1 members.txt > /dev/null
  then
    echo "You are already a member"
    exit 1
  else 
    echo $1,$2,NewMem > members.txt
    echo The new members list is
    cat members.txt
  fi
else 
  echo "Usage: addmember email@address full_name"
  exit 1
fi

The cake script also gets modified:

#!/bin/bash
#who is the caker man today
caker=$(head -1 roster.txt | cut -d',' -f2)
caker_email=$(head -1 roster.txt | cut -d',' -f1)
echo The roster man today is $caker
echo His email address is $caker_email
echo
#looping through members.txt list and sending emails
while read line
do
  email=$(echo $line | cut -d',' -f1)
  fn=$(echo $line | cut -d',' -f2 | cut -f1)
  echo Hello, ${fn}!
  echo Remind you that the cake club is meeting today at 10.30 am.
  if [ $caker_email == $email ]
  then
    echo This time it\'s your turn to bring the cake!
  fi
  echo See you there.
  echo The Cake club manager, `whoami`
  #to simulate sending the email
  echo "(sending to $email)"
  echo
done

#update the roster
grep NewMem members.txt | cut -d',' -f1,2 >> roster.txt
#using the necessary option to only pick all lines but the last one in the file
tail +2 roster.txt > tmp.txt
head -1 roster.txt >> tmp.txt
mv tmp.txt roster.txt
echo The new roster is
cat -n roster.txt

Step 3: A Final Touch

This can be a bit hard.

Modify the cake script to eliminate "sending emails" if the script has already been run recently: check whether the roster.txt file was modified during last 7 days (assume that the roster.txt file can be only modified by the cake script).

Solution to Step 3 (discussed orally)

Result

	#!/bin/bash
	#checking if the script has been run recently
	find .	-mtime +8 | grep roster > /dev/null
	if [ $? -eq 1 ]
	then
	  echo The reminder has been already sent.
	  exit 1
	fi
	#who is the caker man today
	caker=$(head -1 roster.txt | cut -d',' -f2)
	caker_email=$(head -1 roster.txt | cut -d',' -f1)
	echo The roster man today is $caker
	echo His email address is $caker_email
	echo
	while read line
	do
	  email=$(echo $line | cut -d',' -f1)
	  fn=$(echo $line | cut -d',' -f2 | cut -f1)
	  echo Hello, ${fn}!
	  echo Remind you that the cake club is meeting today at 10.30 am.
	  if [ $caker_email == $email ]
	  then
	    echo This time it\'s your turn to bring the cake!
	  fi
	  echo See you there.
	  echo The Cake club manager, `whoami`

	  # simulate sending the email
	  echo "(sending to $email)"
	  echo
	done
	#update the roster
	grep NewMem members.txt | cut -d',' -f1,2 >> roster.txt
	tail +2 roster.txt > tmp.txt
	head -1 roster.txt >> tmp.txt
	mv tmp.txt roster.txt
	echo The new roster is
	cat -n roster.txt
	#Making new members into regular members
	grep -v NewMem members.txt > tmp.txt
	grep NewMem members.txt | cut -d',' -f1,2 >> tmp.txt
	mv tmp.txt members.txt