shell实例浅谈之十检测整数、浮点数和日期数的合法性

一、问题

      实际工作中,经常会碰到对数值的检测,在此将检测整数、浮点数和日期的合法性综合在一起,总结各种不同的检测方法。特别是日期总结了shell处理日期非常好的方法,可以借鉴使用,每个函数都可以根据需要独立提取出来。

二、详解

(1)检测输入整数的合法性

正负数的判断,范围的判断。可为负数,参数可为1~3个(后两个表范围)。

#!/bin/bash
validint()
{
    signed=""     #初始化
    integer=$1  min=$2  max=$3
    #判断是否为负数,第一个字符是否为-。如是,则判断后面的是否为整数;若非负,则判断是否为整数。
    if [ "$(echo $1 | cut -c1)" = "-" ] ; then
        signed="-"
        integer="${integer#?}" #${variable#pattern}把variable中的内容去掉左边最短的匹配模式,?表示仅与一个任意字符匹配
    fi
    if [ -z "$integer" ]; then   #只有一个符号没有数字是非法的
        echo "Invalid input, just a '-' is not allowed" >&2  #注意:>&2 三个字符紧连。
		    return 1
    fi
    #判断是否都是数字组成。
	  if [ -n "$(echo $integer | sed 's/[[:digit:]]//g')" ] ; then  #判断数字的常用方法
		    echo "Invalid integer, it includes some char but digit" >&2
		    return 1
	  fi
    integer="$signed$integer"
    #范围判断,当min和max为空时(即未传范围),默认为$integer。
    if [ $integer -lt ${min:=$integer} ] ;then   #:=代表若min值为空则赋值成默认的integer,若min已被赋值则为不再以integer赋值。等价于[ -z $min -a min=$integer ];if [ $integer -lt min ] ; then。
		    echo "$integer is too small,it should greater than $min" >&2
		    return 1
	  fi
	  if [ $integer -gt ${max:=$integer} ] ;then
    #等价于[ -z $max -a max=$integer ];if [ $integer -gt max ] ; then。
		   echo "$integer is too large,it should little than $max" >&2
		   return 1
	  fi
    return 0
}

####函数入口####
#判断传参的合法性
if [ $# -eq 0 ]
then
    echo "you could not do nothing" >&2
    exit 1
fi

if validint "$1" "$2" "$3"    #三个参数,$2和$3表范围可以缺省
then
    echo "your input integer is valid"
fi

 
 

(2)检测输入浮点数的合法性

       思路:首先根据小数点判断是整数还是浮点数,无小数点作整数判断处理;有小数点使用cut将浮点数分成两部分,前半部分为合法整数并且后半部分为>=0的整数才得出浮点数合法的结论。例如-1.2、1.23、-.23都是合法的浮点数。

#检测浮点数的合法性,不支持科学表示法
#!/bin/bash
validfloat()
{
    fvalue=$1
	  if [ -z "$fvalue" ] ; then        # 保证传入参数非空
		    echo "you input nothing" >&2
		    return 1
    fi
    #判断有点否(判断方法是非.号替换法)
    if [ -n "$(echo $fvalue | sed 's/[^.]//g')" ]; then
        integer="$(echo $fvalue | cut -d. -f1)"
        decimal="$(echo $fvalue | cut -d. -f2)"

        #判断整数部分的合法性,当-.75等为合法
        if [ "$integer" != "-" ] ; then
			      if ! validint "$integer" ; then
				        echo "part before dot $integer is not valide!" >&2
				        return 1
			      fi
		    fi
        #判断小数部分整数的合法性。必须>=0, 当为空时不判断。
        if [ -n "$decimal" ]; then
            if ! validint "$decimal" 0; then
                echo "part after dot $decimal is not valide!" >&2
                return 1
            fi
        fi
    else     #没有小数点,就当作整数处理
        if validint "$fvalue"; then
            echo "your input is a integer" >&2
            return 1
        fi
    fi  
}
#此函数在判断整数时已完成
validint()
{
    signed=""     #初始化
    integer=$1  min=$2  max=$3
    #判断是否为负数,第一个字符是否为-。如是,则判断后面的是否为整数;若非负,则判断是否为整数。
    if [ "$(echo $1 | cut -c1)" = "-" ] ; then
        signed="-"
        integer="${integer#?}" #${variable#pattern}把variable中的内容去掉左边最短的匹配模式,?表示仅与一个任意字符匹配
    fi
    if [ -z "$integer" ]; then   #只有一个符号没有数字是非法的
        echo "Invalid input, just a '-' is not allowed" >&2  #注意:>&2 三个字符紧连。
		    return 1
    fi
    #判断是否都是数字组成。
	  if [ -n "$(echo $integer | sed 's/[[:digit:]]//g')" ] ; then  #判断数字的常用方法
		    echo "Invalid integer, it includes some char but digit" >&2
		    return 1
	  fi
    integer="$signed$integer"
    #范围判断,当min和max为空时(即未传范围),默认为$integer。
    if [ $integer -lt ${min:=$integer} ] ;then   #:=代表若min值为空则赋值成默认的integer,若min已被赋值则为不再以integer赋值。等价于[ -z $min -a min=$integer ];if [ $integer -lt min ] ; then。
		    echo "$integer is too small,it should greater than $min" >&2
		    return 1
	  fi
	  if [ $integer -gt ${max:=$integer} ] ;then
    #等价于[ -z $max -a max=$integer ];if [ $integer -gt max ] ; then。
		   echo "$integer is too large,it should little than $max" >&2
		   return 1
	  fi
    return 0
}

#####函数入口#####
if validfloat $1; then          #判断浮点数
    echo "$1 is a valid floating-point value"
fi
exit 0

(3)检测输入日期的合法性

日期中包括五部分内容,检测日期合法性、判断润年、获取当月天数、输出昨天日期和输出明天日期。支持日期的多种输入格式。

#!/bin/sh
######################################
#SHELL日期计算函数                   #
#1:判断是否闰年check_leap()           #
#2:获取月份最大日期get_mon_days()     #
#3:检查日期格式check_date()           #
#4:返回昨天日期get_before_date()     #
#5:返回明天日期get_next_date()       #
######################################

#-----------------------------------------------------------------
#判断是否闰年
#input:year
#output: "true" "fase"
check_leap()
{
    Y=`expr substr $1 1 4`

    r1=`expr $Y \% 4`     #if((year%4==0&&year%100!=0)||year%400==0)
    r2=`expr $Y \% 100`   #(1)普通年能被4整除且不能被100整除的为闰年,(2)世纪年能被400整除的是闰年。
    r3=`expr $Y \% 400`

    if [ "$r1" -eq 0 -a "$r2" -ne 0 -o "$r3" -eq 0 ];then
        FRUN="true"
    else
        FRUN="false"
    fi
    echo $FRUN
}
#-----------------------------------------------------------------

# 获取月份最大日期
#方法1
get_mon_days()
{
    Y=`expr substr $1 1 4`
    M=`expr substr $1 5 2`

    case "$M" in
         01|03|05|07|08|10|12) days=31;;
         04|06|09|11) days=30;;
         02)
        _tmpStr=`check_leap "$Y"`  #判断是否闰年
        if [ "$_tmpStr" = "true" ] ; then
            #闰年
            days=29
        else
            days=28
        fi
        ;;
         *)
        days=0
        ;;
    esac
    echo $days
}
#-----------------------------------------------------------------
# 获取月份最大日期
#方法2
get_mon_days2()
{
    Y=`expr substr $1 1 4`    #取4位数表示年
    M=`expr substr $1 5 2`    #取2位数表示月

    #取当月底最后一天
    aa=`cal $M $Y` #日历       #cal命令
    days=`echo $aa | awk '{print $NF}'`  #awk '{print $NF}'输出每行最后一个域的内容,区别于awk '{print NF}'空格分割的域数量
    echo $days
}

#检查日期格式(例:20141022)
#返回状态($?) 0 合法 1 非法
check_date()
{
    [ $# -ne 1 ] && return 1     #检查是否传入一个参数

    _lenStr=`expr length "$1"`  #检查字符串长度
    [ "$_lenStr" -ne 8 ] && return 1

    #检查是否输入的是非0开头的数字
    _tmpStr=`echo "$1" | grep "^[^0][0-9]*$"`
    [ -z "$_tmpStr" ] && return 1

    Y=`expr substr $1 1 4`
    M=`expr substr $1 5 2`
    D=`expr substr $1 7 2`
    #检查月份
    [ "$M" -lt 1 -o "$M" -gt 12 ] && return 1
    #取当月天数
    days=`get_mon_days "$Y$M"`
    #检查日
    [ "$D" -lt 1 -o "$D" -gt "$days" ] && return 1

    return 0
}
#-----------------------------------------------------------------

#返回昨天日期
get_before_date()
{
    Y=`expr substr $1 1 4`
    M=`expr substr $1 5 2`
    D=`expr substr $1 7 2`

    #某月01日的情况
    if [ "$D" -eq 01 ]
    then
        if [ "$M" -eq 01 ]
        then
            #某年01月01日的情况
            #取上年年底日期(12月31日)
            YY=`expr $Y - 1`
            be_date="${YY}1231"
        else
            #取上个月月末日期
            MM=`expr $M - 1`
            MM=`printf "%02d" $MM`
            dad=`get_mon_days "$Y$MM"`
            be_date="$Y$MM$dad"
        fi
    else
        #通常情况
        DD=`expr $D - 1`
        DD=`printf "%02d" $DD`
        be_date="$Y$M$DD"
    fi
    echo $be_date
}
#-----------------------------------------------------------------

#返回明天日期
get_next_date()
{
    Y=`expr substr $1 1 4`
    M=`expr substr $1 5 2`
    D=`expr substr $1 7 2`

    dad=`get_mon_days "$Y$M"`  #当月天数

    if [ "$D" = "$dad" ];then
        #特殊情况:月底
        if [ "$M$D" = "1231" ];then
            #年底的情况
            YY=`expr $Y + 1`
            next_date="${YY}0101"
        else
            MM=`expr $M + 1`
            MM=`printf "%02d" $MM`
            next_date="$Y${MM}01"
        fi
    else
        #通常情况
        DD=`expr $D + 1`
        DD=`printf "%02d" $DD`
        next_date="$Y$M$DD"
    fi

    echo $next_date
}
#----------------------------main入口-------------------------------------
if [ $# -ne 1 ]
then
    echo "<ERROR>Usage:`basename $0` year-month-day or year/month/day or yearmonthday"
    exit 1
fi

echo "日期:$1"
set -- $(echo $1 | sed 's/[\/\-]/ /g')  #处理MM/DD/YYYY or MM-DD-YYYY 的形式,set 的--命令可以将其后的参数赋予位置参数$1、$2和$3
#检查日期格式
_dateStr="$1$2$3"
check_date $_dateStr
if [ $? -eq 1 ];then
    echo "<ERROR>输入日期[$1]格式错误!示例:(`date +%Y%m%d`)"
    exit 1
fi

cat <<EOF
     是否闰年: `check_leap $_dateStr`
     当月天数: `get_mon_days2 $_dateStr`
     昨天日期: `get_before_date $_dateStr`
     明天日期: `get_next_date $_dateStr`
EOF

三、总结

(1)数字、日期和字符串的处理都是很常见的问题,字符串输入为字母数字的判断在第七章中已介绍。

(2)若只是判断日期的合法性,则可抽取部分代码,判断年、月的天数和日的范围即可。

(3)本代码若有不完善的地方,可请大家留言,也可联系本人yang.ao@i-soft.com.cn。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乌托邦2号

博文不易,支持的请给予小小打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值