Korn Shell: Unix and Linux Programming Manual, Third Edition, The By Anatole Olczak
Table of Contents
Appendix D. Sample Korn Shell Scripts
Calendar Program - kcal
This is a Korn shell script that implements a menu-driven calendar program. It supports addition, deletion, modification, and listing of calendar entries. It also provides the ability to find the calendar entry for the current day and list all calendar entries.
#!/bin/ksh## kcal - Korn Shell calendar program## Process errorsfunction error {print ${1:-"unexplained error encountered"}exit ${2}}# Check argumentsif (($# > 0))thenerror "Usage: $0" 1fi# Use environment variable setting or default: ${CALFILE:=$HOME/.calfile}# Create calendar file if non-existent; flag# creation errorif [[ ! -f $CALFILE ]]thenprint "Creating default $HOME/.calfile"> $HOME/.calfile || error "$HOME/.calfile: \cannot create" 1fi# Variable declaration/assignmenttypeset DATE= LINE= ENTRY= REPLY="yes" \PAGER=$(whence more) CLEAR=$(whence clear)# Set trap to not allow interruptstrap '$CLEAR; print "\aInterrupt ignored - use \menu to quit. Press <Return> to continue."; \read TMP; $CLEAR' INT QUIT# Set EXIT trap - perform cleanuptrap 'rm -rf /tmp/.FOUND$$ /tmp/.CHANGE$$ \/tmp/.DEL$$' EXIT# Check the datefunction checkdate {while truedo# Prompt for dateread DATE?"Enter date in mmdd[yy] format (defaulttoday):"case $DATE in# Default - use todays date"" ) DATE=$(date +%m-%d-%y)break ;;# Check the given date+([0-9]) )case ${#DATE} in4|6)# Set month to first 2 charstypeset -L2 MO=$DATE# Check length for year;# 4 = mmdd, 6 = mmddyyif ((${#DATE} == 6))then# Set TMP to get datetypeset -L4 TMP=$DATE# Get daytypeset -R2 DA=$TMP# Get yeartypeset -R2 YR=$DATEelse# Get daytypeset -R2 DA=$DATE# Set to current yearYR=$(date +%y)DATE=$DATE$YRfi# Now check indiv values# Day must be in range 01-31if ((DA < 01 || DA > 31))thenprint "$DA: invalid day \format - try again"continuefi# Month must be 01-12if ((MO < 01 || MO > 12))thenprint "$MO: invalid \month format - try again"continuefi# Set date format mm-dd-yyDATE=$MO-$DA-$YRbreak ;;* ) # Invalid formatprint "$DATE: invalid date \format - try again" ;;esac ;;# Invalid date given* ) print "$DATE: invalid format - try again" ;;esacdone}# Add new calendar entryfunction addentry {$CLEARENTRY="$DATE"# For existent entry, just add more dataCOUNT=$(grep -c "^$DATE" $CALFILE)if ((COUNT > 0))thenchangeentryreturnfi# Prompt for inputprint "Enter info for $DATE: (enter <Return> by itselfwhen finished)"while truedoread LINE?"=>"if [[ -n $LINE ]]thenENTRY="$ENTRY,$LINE"elsebreakfidone# Append to calendar fileprint $ENTRY>>$CALFILE# Sort the calendar filesort -o $CALFILE $CALFILE}function formatentry {$CLEARtypeset IFS="," \BORDER="**************************************" \BORDER1="* *" \FILE=$1if [[ -s $FILE ]]then# Open calendar file for reading, and# format output(exec 0<$FILEwhile read -r ENTRYdoprint "$BORDER\n$BORDER1"set $ENTRYtypeset -L35 LINE="DATE: $1"print "* $LINE*"shiftprint "$BORDER1"for idoLINE="$i"print "* $LINE*"doneprint "$BORDER1"doneprint "$BORDER") | $PAGERelseprint "No entries found."fi# Prompt to continueuntil [[ $REPLY = "" ]]doread REPLY?"Enter <Return> to continue..."done}# Find specific entryfunction findentry {$CLEAR# Check for entry - put it in temp found filegrep $DATE $CALFILE >/tmp/.FOUND$$# Format found entriesformatentry /tmp/.FOUND$$}# Change an entryfunction changeentry {# Find specific entry - put it in temp found filegrep $DATE $CALFILE | tr ',' '\012'>/tmp/.FOUND$$# Return if no entry was foundif [[ ! -s /tmp/.FOUND$$ ]]then$CLEARread TMP?"Entry for $DATE not found - press<Return> to continue"returnfi# Prompt again for changewhile [[ $REPLY != "" ]]doread REPLY?"Change/Add to entry for <$DATE>?"case $REPLY in[yY]* | "" )break ;;[nN]* ) print "Ok, aborting entrychange"return ;;*) print "Invalid reply - try again." ;;esacdone# Edit the temporary found file${EDITOR:-vi} /tmp/.FOUND$$# Remove the specified original entrygrep -v $DATE $CALFILE > /tmp/.CHANGE$$# Put back new change in record format.# Add trailing \n(cat /tmp/.FOUND$$ | tr '\012' ',' ; print ) \>>/tmp/.CHANGE$$# Put back new filecat /tmp/.CHANGE$$ > $CALFILE# Clean up tmp filesrm -rf /tmp/.CHANGE$$ /tmp/.FOUND$$}# Remove specific entryfunction delentry {# Look for entrygrep $DATE $CALFILE >/tmp/.FOUND$$# Return if not foundif [[ ! -s /tmp/.FOUND$$ ]]then$CLEARread TMP?"Entry for $DATE not found - press<Return> to continue"returnfi# Prompt to deletewhile [[ $REPLY != "" ]]doread REPLY?"Delete entry for <$DATE>?"case $REPLY in[yY]* | "")break ;;[nN]*)print "ok, aborting delete";return ;;*) print "Invalid reply - try again.";;esacdone# Merge changes - put them in temporary filegrep -v $DATE $CALFILE > /tmp/.DEL$$# Put back new filecat /tmp/.DEL$$ > $CALFILE# Clean up tmp filesrm -rf /tmp/.DEL$$ /tmp/.FOUND$$}Set menu selection promptPS3="Enter selection or <Return> for default menu:"# Display menuwhile truedo$CLEARselect i in "Add calendar entry" "Delete calendarentry" "Change calendar entry" "Find calendar entry" "Listall calendar entries" "List todays calendar entry" "Exit"docase $i in"Add calendar entry")checkdateaddentry$CLEAR ;;"Delete calendar entry")checkdatedelentry$CLEAR ;;"Change calendar entry")checkdatechangeentry$CLEAR ;;"Find calendar entry")checkdatefindentry$CLEAR ;;"List all calendar entries")formatentry $CALFILE$CLEAR ;;"List todays calendar entry")DATE=$(date +%m-%d-%y)findentry$CLEAR ;;"Exit")exit ;;* ) print "\aInvalid selection \c"read TMP?"- press <Return> tocontinue"$CLEARcontinue ;;esacdonedone