第一章:The Missing Code Library--3.正常化日期格式

   Shell脚本开发过程中的一个不确定的问题就是前后不一致的数据格式的数目。要正常化他们,可能很难也可能比较简单。日期格式就是最有挑战性的一种,因为一个日期可以有多种特定方式的写法。即使你提出了一个特定的格式,比如“月份 几号 年份”,也有可能得到一个不符合条件的输入:月份是用的数字,而不是名称,或是月份的名称用的是一个缩写(英文中比如11月是Nov.),再或是一个全是大写的完整月份名。由于这个原因,一个正常化日期的函数,虽然自身还只是有点初级功能,但是以后会证实在一个更完备的脚本中会是多么的有用处,特别是第7个脚本--正确化日期格式。

normdata.sh
 1 #!/bin/sh
 2 
 3 # normdata.sh -- 在日期中将月份正常化为3个字母,第一个字母大写。
 4 # 对于第7个脚本valid-date很有帮助的函数。
 5 
 6 monthnoToName()
 7 {
 8     # 把变量'month'设置为一个适当的值
 9     case $1 in
10         1 ) month="Jan";;  2 ) month="Feb";; 
11         3 ) month="Mar";;  4 ) month="Apr";; 
12         5 ) month="May";;  6 ) month="Jun";; 
13         7 ) month="Jul";;  8 ) month="Aug";; 
14         9 ) month="Sep";;  10) month="Oct";; 
15         11) month="Nov";;  12) month="Dec";;
16         * )echo "$0: Unknown numeric month value $1" >&2; exit 1
17     esac
18     return 0
19 }
20 
21 ### 脚本的主要部分
22 
23 if [ $# -ne 3 ]; then
24     echo "Usage: $0 month day year" >&2
25     echo "Typical input formats are August 3 1962 and 8 3 2002" >&2
26     exit 1
27 fi
28 
29 if [ $3 -lt 99 ] || [ $3 -gt 9999 ]; then
30     echo "$0: expected four-digit year value." >&2; exit 1
31 fi
32 
33 if [ -z $(echo $1 | sed 's/[[:digit:]]//g') ]; then
34     monthnoToName $1
35 else
36     # 正常化开头的3个字母,第一个大写,剩下的小写
37     month="$(echo $1 | cut -c1 | tr '[:lower:]' '[:upper:]')"
38     month="$month$(echo $1 | cut -c2-3 | tr '[:upper:]' '[:lower:]')"
39 fi
40 
41 echo $month $2 $3
42 
43 exit 0

   脚本如何工作
   注意第3个条件语句:

if [ -z $(echo $1 | sed 's/[[:digit:]]//g') ]; then

   它去掉了所有的数字,然后使用-z来测试看看结果是不是为空。如果结果为空,第一个输入域肯定是一个或多个数字,所以调用函数monthnoToName后将它映射到了一个月份的名字上。如果不为空,通过2个子shell转义序列(即由$符号和左右圆括号括起来的序列,这样的目的是调用封装的命令,然后将输出替换掉),一个由cut和tr组成的管道就会用来生成月份。第一个序列仅仅是提取了第一个字符,然后用tr命令将它大写化。(注:序列echo $1 | cut -c1也
可以写成${1%${1#?}}。)第二个序列提取了第2和第3个字符,然后将它们小写化。

   运行脚本:
   为了确保对将来包含了normdata功能的脚本最大的适应性,这个脚本被设计成可以在命令行接受3个域的输入。如果你仅仅是想要交互式的使用这个脚本,相比之下,你要提示用户是3个域,不过这样的话,要是想从别的脚本调用normdate的话就不大方便了。

   结果:
   这个脚本完成了我们所期望的:只要日期格式相对简单点,就可以将它正常化,(已知的月份名字,月份的值在1-12之间,还有一个4个数字的年份)。比如:

结果
1 ./normdate.sh 8 3 62
2 ./normdate.sh: expected four-digit year value.
3 ./normdate.sh 8 3 1962
4 Aug 3 1962
5 ./normdate.sh AUGust 3 1962
6 Aug 3 1962

   延伸阅读:
   在你因为自己可以添加很多的扩展到这个脚本中而变得太兴奋之前,可以看看脚本7,它使用了normdate来正确话输入的日期。不管怎样,你现在就可以做的一个改动是,马上增加下面的一小段代码到脚本的开头测试部分,你的脚本就可以接受形式为MM/DD/YYYY或MM-DD-YYYY格式的日期了:

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

测试下:

1 ./normdate.sh March-11-1911
2 Mar 11 1911
3 ./normdate.sh 8/3/1962
4 Aug 3 1962

最后的脚本是:

完整的normdate.sh
 1 #!/bin/sh
 2 
 3 monthToName()
 4 {
 5     case $1 in
 6         1 ) month="Jan";;  2 ) month="Feb";; 
 7         3 ) month="Mar";;  4 ) month="Apr";; 
 8         5 ) month="May";;  6 ) month="Jun";; 
 9         7 ) month="Jul";;  8 ) month="Aug";; 
10         9 ) month="Sep";;  10) month="Oct";; 
11         11) month="Nov";;  12) month="Dec";;
12         * )echo "$0: Unknown numeric month value $1" >&2; exit 1
13     esac
14     return 0
15 }
16 
17 if [ $# -eq 1 ]; then
18     set -- $(echo $1 | sed 's/[\/\-]/ /g')
19 fi
20 
21 if [ $# -ne 3 ]; then
22     echo "Usage: $0 month day year" >&2
23     echo "Typical input formats are August 3 1962 and 8 3 2002" >&2
24     exit 1
25 fi
26 
27 if [ $3 -lt 99 ] || [ $3 -gt 9999 ]; then
28     echo "$0: expected four-digit year value." >&2; exit 1
29 fi
30 
31 if [ -z $(echo $1 | sed 's/[[:digit:]]//g') ]; then
32     monthToName $1
33 else
34     month="$(echo $1 | cut -c1 | tr '[:lower:]' '[:upper:]')"
35     month="$month$(echo $1 | cut -c2-3 | tr '[:upper:]' '[:lower:]')"
36 fi
37 
38 echo $month $2 $3
39 
40 exit 0
posted @ 2012-11-30 15:19  十舍七匹狼  阅读(988)  评论(0)    收藏  举报