03-规范日期格式

需求分析

shell脚本开发存在的一个问题是各种不一致的数据格式。规范数据格式的难度可小可大。数据格式算是其中最有挑战性的工作之一,这是因为指定日期的方法各种各样。哪怕是提示过特定的格式,例如按照“月-日-年”,照样有可能得到不一致的输入:月份没有采用数字,而是用了月份名称或月份名称缩写,甚至还有全部是大写字母的月份全称。有鉴于此,一个能够规范日期的函数,尽管本身很基础,却能在后续的脚本编写工作中帮上大忙。

代码实现

以下代码可以对符合一组简单要求的日期进行规范:月份要么采用名称,要么采用1至12之间的值;年份必须采用4位数的值。规范后的日期由月份名称(3个字母的缩写)、天数以及4位数的年份组成。

#!/bin/bash
# filename:normdate
# function:将月份规范成3个字母,首字母大写
# description:该脚本随后将作为脚本7的辅助函数。如果没有错误,那么以0值退出。

monthNumToName(){
	# 将变量month设置为相应的值
	case $1 in
		1) month="Jan"	;;	2) month="Feb"	;;
		3) month="Mar"	;;	4) month="Apr"	;;
		5) month="May"	;;	6) month="Jun"	;;
		7) month="Jul"	;;	8) month="Aug"	;;
		9) month="Sep"	;;	10) month="Oct"	;;
		11) month="Nov"	;;	12) month="Dec"	;;
		*) echo "$0:Unknown numeric month value $1" >&2	exit
	esac
	return 0		
}

# 主脚本开始:如果要将该脚本包含到其他脚本之内,那么删除或注释掉本行以下的所有内容。
# 输入验证
if [ $# -ne 3 ];then
	echo "Usage: $0 month day year" >&2
	echo "Formats are August 3 1962 and 8 3 1962" >&2
	exit 1
fi
if [ $3 -le 99 ];then
	echo "$0: expected 4-digit year value." >&2
	exit 1
fi

# 输入的月份是否为数字
if [ -z $(echo $1 | sed 's/[[:digit:]]//g') ];then
	monthNumToName $1
else
	# 规范前3个字母,首字母大写,其余小写。
	month="$(echo $1 | cut -c1 | tr '[:lower:]' '[:upper:]')" 
	month="$month$(echo $1 | cut -c2-3 | tr '[:upper:]' '[:lower:]')"
fi

echo $month $2 $3
exit 0
运行脚本

注意脚本中的[ -z $(echo $1 | sed 's/[[:digit:]]//g') ]。它移除了第一个输入字段中的所有数字,然后使用-z检测结果是否为空。如果为空,则意味着该字段中只有数字,可以使用函数monthNumToName将其直接映射成月份名称,该函数还会验证数字代表的月份是否有效。

否则,我们假定字段中包含的是月份字符串,这得借助两个子shell,通过复杂的cut和tr管道来实现规范化。

  • 第一个子shell:$(echo $1 | cut -c1 | tr '[:lower:]' '[:upper:]')只提取出输入的首个字符,然后使用tr将其转换成大写(命令序列echo $1|cut -c1也可以写成POSIX形式的${1%${1#?}})。
  • 第二个子shell:$(echo $1 | cut -c2-3 | tr '[:upper:]' '[:lower:]')提取出后两个字符并将其转换成小写,这样就生成了首字母大写的月份缩写(长度为3个字符)。

注意:这种字符串操作方法并没有检查输入的月份是否有效,这一点和月份为数字时的处理并不一样。


为了确保以后引入了normdate功能的脚本拥有最大的灵活性,这个脚本被设计成以3个命令行参数的形式接收输入。如果你只打算将其用于交互模式,那就得提示用户输入这些参数,不过这样一来,要想在其他脚本中调用normdate就更困难了。

$ sh normdate.sh 8 3 62
# -> normdate: expected 4-digit year value.

$ sh normdate.sh 8 3 1962
# -> Aug 3 1962

$ sh normdate.sh AUGUST 03 1962
# -> Aug 03 1962

注意:该脚本只能规范月份描述,天数格式(例如包含前导数字0的天数)和年份保持不变。

精益求精

在你兴奋于能够为该脚本添加大量扩展,使其变得更为复杂之前,先看看脚本07,它用到了normdate来验证输入日期。

可以做出的一处改动是允许脚本接受形如MM/DD/YYYY或MM-DD-YYYY的日期格式,将下面的代码添加到第一个条件语句之前。

if [ $# -eq 1 ];then
	set -- $(echo $1 | sed 's/[\/\-]/ /g')
fi

修改完之后,就可以输入并规范下列常见格式了:

$ sh normdate.sh 6-10-2000 			#-> Jun 10 2000
$ sh normdate.sh March-11-1911	#-> Mar 11 1911
$ sh normdate.sh 8/3/1962				#-> Aug 3 1962

如果你仔细阅读代码,会意识到还可以采用更复杂的改进方法,使其能够验证指定日期中的年份以及各种国际日期格式。这些就作为练习吧!

你可能感兴趣的:(Shell,脚本实战,Shell,Linux)