A solution to a bash script problem from the lab exam 2006 is discussed.
The CS department at ANU runs a cake club. Members meet every second Tuesday, at 10.30 am in the commons room and drink coffee or tea with a cake provided by one of the members. The club has a manager whose responsibility is to notify every club member about every 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:
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 ... ... ....
As you can see, there are two categories: honorary members (marked by HonMem)
and regular members (marked by the empty field). Only the regular members
have to provide a cake
for every meeting. The cake duty is regulated by the
roster.txt file, which consists of all
regular members. The format of the roster.txt file is the
same as members.txt but without the third field (the status).
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 shell-prompt> cat roster.txt u1234456@anu.edu.au,Alexei B Khorev u2345678@anu.edu.au,Ian Barnes chris.johnson@anu.edu.au,Chris Johnson ... ... ....
We rely on the constraint (which will be assumed throughout the whole exercise) that neither email address, nor any part of the full name contain the pattern "HonMem". The first member on the list is the person who has to bring cake for the next club meeting.
The task is to write a bash script called cake which will:
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 u2345678@anu.edu.au,Ian Barnes chris.johnson@anu.edu.au,Chris Johnson u1234456@anu.edu.au,Alexei B Khorev
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
#updating the roster
#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 roster.txt
To enter the cake club is much easier than to enter a masonic lodge. You have to simply ask the club manager to add your email address and name to the members.txt file. Being a computer professional, the club manager will do it with another bash script. Your task in this part is to write such script. Call it addmember. The script will take two command line arguments — email address and the full name. Since the full name can contain more than one word, the second argument can be provided with quotation marks. The addmember script will be run as follows:
shell-prompt> ./addmember henry.gardner@anu.edu.au "Henry Gardner"
The new member will be added to the members.txt file with a temporary status NewMem. Therefore after the executing the above command, the members.txt file will look like this:
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 henry.gardner@anu.edu.au,Henry Gardner,NewMem
If the person who wishes to enter the club is already a member
then the request must be rejected. As the ID of membership, the
addmember script will use the email address (the first field).
The attempt to add a duplicate member will be rejected, and
and the members.txt file will remain unchanged.
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
#updating 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 roster.txt
This can be a bit hard.
Modifiy the cake script to eliminate "sending emails" if the script
has already been run recently. Before executing all the commands in the cake
script, check whether the roster.txt file was modified
during last 7 days (the roster.txt file can be only modified
by the cake script, but not by the addmember script).
If the roster.txt file was last modified less than 7 days ago,
the script must abort
all the remaining executions and exit. If
the roster.txt file was last modified more than 7 days ago,
the script must continue and execute all the commands which it contains.
This feature would prevent from unnecessary emailing and —
more importantly — distorting
the roster.txt file.
Hint. You will need to know how to determined whether a file was modified in the last 7 days. Check the find command and the option -mtime (example of such use will be presented in today's lecture).
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`
#to simulate sending the email
echo "(sending to $email)"
echo
done
#updating 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 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