第三章:Creating Utilities--30.记录日程安排

   这个脚本事实上是2个脚本,用来执行一个简单的日历程序。第一个脚本,addagenda.sh,它可以存储两种事件:可重复发生的、一次性发生的。它允许用户指定天数、周或是年。所有的日期都被合法化后保存了起来,同时在家目录下的.agenda文件中还有一行关于事件的描述信息。第二个脚本中,agenda.sh中核实所有的事件,会显示所有安排对应日期的日程。
我觉得这个脚本非常有用,特别是在记录生日和周年纪念的时候。它真的给我减少了很多麻烦。

代码:

addagenda.sh

#!/bin/sh

# addagenda.sh --提醒用户向事件脚本中添加新内容

agendafile="$HOME/.agenda"

isDayName()
{
	# 返回值 0,成功;1,失败

	case $(echo $1 | tr '[[:upper:]]' '[[:lower:]]') in
		sun*|mon*|tue*|wed*|thu*|fri*|sat*) retval=0;;
		*) retval=1;;
	esac
	return $retval
}

isMonthName()
{
	case $(echo $1 | tr '[[:upper:]]' '[[:lower:]]') in
		jan*|feb*|mar*|apr*|may*|jun*) return 0;;
		jul*|aug*|sep*|oct*|nov*|dec*) return 0;;
		*) return 1
	esac
}

normalize()
{
	# 返回的字符串中首字母大写,后续2个小写
	echo -n $1 | cut -c1 | tr '[[:lower:]]' '[[:upper:]]'
	echo $1 | cut -c2-3 | tr '[[:upper:]]' '[[:lower:]]'
}

if [ ! -w $HOME ]; then
	echo "$0: cannot write in your home directory($HOME)" >&2
	exit 1
fi

echo "Agenda: The Unix Reminder Service"
echo -n "Date of event(day mon, day month year, or dayname): "
read word1 word2 word3 junk

if isDayName $word1; then
	if [ ! -z "$word2" ]; then
		echo "Bad dayname format: just specify the day name by itself." >&2
		exit 1
	fi
	date="$(normalize $word1)"
else
	if [ -z "$word2" ]; then
		echo "Bad dayname format: unknown day name specified" >&2
		exit 1
	fi

	if [ ! -z "$(echo $word1 | sed 's/[[:digit:]]//g')" ]; then
		echo "Bad date format: please specify day first, by day number" >&2
		exit 1
	fi

	if [ "$word1" -lt 1 -o "$word1" -gt 31 ]; then
		echo "Bad date format: day number can only be in range 1-31" >&2
		exit 1
	fi

	if ! isMonthName $word2; then
		echo "Bad date format: unknown month name specified." >&2
		exit 1
	fi

	word2="$(normalize $word2)"

	if [ -z "$word3" ]; then
		date="$word1$word2"
	else
		if [ ! -z "$(echo $word3 | sed 's/[[:digit:]]//g')" ]; then
			echo "Bad date format: year value should be 2000-2500" >&2
			exit 1
		elif [ $word3 -lt 2000 -o $word3 -gt 2500 ]; then
			echo "Bad date format: year value should be 2000-2500" >&2
			exit 1
		fi
		date="$word1$word2$word3"
	fi
fi

echo -n "One-line description: "
read description

# Ready to write to data file

echo "$(echo $date | sed 's/ //g') | $description" >> $agendafile

exit 0
agenda.sh
#!/bin/sh

# agenda.sh --查看.agenda文件中是否有今明两天的安排

agendafile="$HOME/.agenda"

checkDate()
{
	# Create the possible default values that'll match today
	weekday=$1 day=$2 month=$3 year=$4
	format1="$weekday" format2="$day$month" format3="$day$month$year"
	# and step through the file comparing dates...

	IFS="|"    # the reads will naturally split at the IFS

	echo "On the Agenda for today:"

	while read data description; do
		if [ "$date" = "$format1" -o "$date" = "$format2" -o "$date" = "$format3" ]; then
			echo "$description"
		fi
	done < $agendafile
}

if [ ! -e $agendafile ]; then
	echo "$0: You don't seem to have an .agenda file." >&2
	echo "To remedy this, please use 'addagenda' to add events" >&2
	exit 1
fi

# Now let's get today's date...

eval $(date "+weekday=\"%a\" month=\"%b\" day=\"%e\" year=\"%G\"")

day="$(echo $day | sed 's/ //g')"    # remove possible leading space

checkDate $weekday $day $month $year

exit 0
addagenda.sh这个脚本,支持3中类型的事件:每周的(比如每个周三)、每年的(比如8月3号),以及一次性的(比如2010年1月10号)。而用户提供的日期会被程序压缩,比如用户给的日期是3 August,压缩后变为3Aug,或是Thursday变成了Thu。完成这个功能的代码是如下函数:
normalize()
{
	# 返回的字符串中首字母大写,后续2个小写
	echo -n $1 | cut -c1 | tr '[[:lower:]]' '[[:upper:]]'
	echo $1 | cut -c2-3 | tr '[[:upper:]]' '[[:lower:]]'
}
agenda.sh脚本通过日期来核实事件。并且该脚本会将这个日期转换为3中可能的日期格式(dayname, day+month, day+month+year)。它仅仅是简单的和.agenda文件中的行比较下。如果有匹配,就显示给用户。
在我看来,最牛逼最狂霸酷炫拽的地方是如何应用一个eval表达式一次性的分配4个日期值给变量的:
eval $(date "+weekday=\"%a\" month=\"%b\" day=\"%e\" year=\"%G\"")
同样,一个接一个提取值也是可以的(比如,weekday="$(date +%a)"),但在有些非常罕见的情况中,这种方法可能会失效:在4个日期请求的中间部分,此时真实日期滚动到了新的一天,因此一个简洁的单一调用更好(注:这个单一的句子就相当于一个原子调用。类似操作系统中的竟态条件)。


运行脚本,测试一下:
$ addagenda 
Agenda: The Unix Reminder Service 

Date of event (day mon, day month year, or dayname): 31 October 
One line description: Halloween 
$ addagenda 
Agenda: The Unix Reminder Service 

Date of event (day mon, day month year, or dayname): 30 March 
One line description: Penultimate day of March 
$ addagenda 
Agenda: The Unix Reminder Service 

Date of event (day mon, day month year, or dayname): Sunday 
One line description: sleep late (hopefully) 
$ addagenda 
Agenda: The Unix Reminder Service 

Date of event (day mon, day month year, or dayname): marc 30 03 
Bad date format: please specify day first, by day number 

$ addagenda 
Agenda: The Unix Reminder Service 

Date of event (day mon, day month year, or dayname): 30 march 2003 
One line description: IM Marv to see about dinner 

Now the agenda script offers a quick and handy reminder of what's happening today: 

$ agenda 
On the Agenda for today: 
Penultimate day of March 
sleep late (hopefully) 
IM Marv to see about dinner 

Notice that it matched entries formatted as day+month, day of week, and day+month+year. For completeness, here's  
the associated .agenda file, with a few additional entries: 

$ cat ~/.agenda 
14Feb|Valentine's Day 
25Dec|Christmas 
3Aug|Dave's Birthday 
4Jul|Independence Day (USA) 
31Oct|Halloween 
30Mar|Penultimate day of March 
Sun|sleep late (hopefully) 
30Mar2003|IM Marv to see about dinner
  这两个脚本在记录日记方面仅仅是起到了一个抛砖引玉的作用。要想搞的更好点,可以在脚本中做一点关于日期的数学运算。另外,如果匹配的日期没有日程安排,可以更人性化的输出一句别的什么内容。

你可能感兴趣的:(linux,shell,shell,bash,scripts,cool,Wicked)