shell例項淺談之十檢測整數、浮點數和日期數的合法性
阿新 • • 發佈:2019-02-18
一、問題
實際工作中,經常會碰到對數值的檢測,在此將檢測整數、浮點數和日期的合法性綜合在一起,總結各種不同的檢測方法。特別是日期總結了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)本程式碼若有不完善的地方,可請大家留言,也可聯絡本人[email protected]。