一.引言
使用 shell 实现时间判断逻辑时,需要判断对应 $hour 是否存在于目标 $ValidHour[*] 数组中,经过各种踩坑,最后用最简单的思路实现了 contains 操作。
二.实现
这里先整理下如何实现,后面会把介绍遇到的各种坑,有兴趣可以看看~
1.需求
任务执行时间为 6,12,18,脚本定时启动并判断是否要在该事件启动后续任务,类似于 crontab
hour=`date +%H`
validHour=('6' '12' '18')
2.shell - contains
function containsHour()
{
hour=$1
validHour=('6' '12' '18')
for _hour in ${validHour[*]}; do
if test $hour -eq $_hour; then
echo true
return
fi
done
echo false
}
# 判断存在
hour=`date +%H`
hourState=`containsHour $hour`
脚本添加 set -x 查看执行过程,思路很简单,通过 for 循环遍历判断字符,如果存在则 return true,否则遍历完毕并返回 false:
+ hour=10
++ containsHour 10
++ hour=10
++ validHour=('6' '12' '18')
++ for _hour in '${validHour[*]}'
++ test 10 -eq 6
++ for _hour in '${validHour[*]}'
++ test 10 -eq 12
++ for _hour in '${validHour[*]}'
++ test 10 -eq 18
++ echo false
+ hourState=false
三.踩坑
1.[] 与 [[]] 使用差别
[] : [] 表达式内支持字符串比较,但不支持数字直接 >,< 比较,需要使用转义符 \>,\< 才能判断数字大小,其次内部使用逻辑判断不能使用 &&,||,内部只支持 -a,-o 类似操作,不支持正则操作
正确操作✅ :
if [ 3 \> 2 -a 5 \> 4 ];then
echo true
else
echo false
fi
+ '[' 3 '>' 2 -a 5 '>' 4 ']'
+ echo true
true
错误操作1 ❎ :
-a 符号改为 &&,无法识别
+ '[' 3 '>' 2
cron.sh: line 71: [: missing `]'
+ echo false
false
错误操作2 ❎ :
>,< 未添加转义符,运算符当成 IO 操作符,运算失效
+ '[' 3 -a 5 ']'
+ echo true
true
[[]] : [[]] 为 [] 操作的扩充,常用于逻辑判断,由于 [] 特殊字符需要使用转义且条件过多时代码不易阅读,所以可以采用 [[]] 操作,其内部运算符不需要转义,对应的逻辑符号为 &&,||,内部不在支持 -a,-o 等操作,但支持正则操作 例如 [a-z].txt [1-9].sh
正确操作 ✅ :
if [[ 3 > 2 && 5 > 4 ]];then
echo true
else
echo false
+ [[ 3 > 2 ]]
+ [[ 5 > 4 ]]
+ echo true
true
错误操作 ❎ :
&& 换为 -a
cron.sh: line 71: syntax error in conditional expression
cron.sh: line 71: syntax error near `-a'
cron.sh: line 71: `if [[ 3 > 2 -a 5 > 4 ]];then'
正则操作 ✅ :
if [[ "a.txt" == *.txt ]];then
echo ture
else
echo false
fi
+ [[ a.txt == *.txt ]]
+ echo ture
ture
Tips: 如果 [[]] 换成 [] 或者将 *.txt 用 "*.txt" 包围则正则失效
2.字符相等判断
除了逻辑符号与 [] 的使用外,字符串判断也有问题
正确操作 ✅ :
a="a"
b="a"
if [ $a = $b ];then
echo ture
else
echo false
fi
可以使用 =,== 判断两个字符是否相等,除此之外数字也可以用该符号判断
错误操作 ❎ :
a="a"
b="a"
if [ $a -eq $b ];then
echo ture
else
echo false
fi
cron.sh: line 85: [: a: integer expression expected
-eq 用于数字比较,所以字符判断会一直返回 false
3.数组表示
for 循环遍历数组时,需要数组之间字符不需要 ',' 逗号隔开,否则会将逗号作为字符的一部分
正确操作 ✅ :
validHour=('6' '12' '18')
错误操作 ❎ :
validHour=('6', '12', '18')
for _hour in ${validHour[*]}; do
echo $_hour
6,
12,
18
6, 12, 变成了新的字符,所以判断 6 == 6, 12=12, 总是 false,让人怀疑人生
4.=~ 正则表达式判断
错误操作 ❎ :
判断元素是否存在时,有给出 =~ 的 demo,比较简洁:
hour='9'
validHour=('6' '12' '18')
if [[ "${validHour[@]}" =~ "$hour" ]];then
echo $hour Exist
fi
“=~” 用于判断string和右边的正则表达式pattern是否匹配,如果需要做两个变量的包含关系可以使用,上述方法执行没有问题
+ hour=9
+ validHour=('6' '12' '18')
+ [[ 6 12 18 =~ 9 ]]
+ echo 9 Not Exist
9 Not Exist
但是当把 9 换成 1时,出现问题,1这个字符存在于数组变量中,即判断为 true,与 contains 的目标不符合,contains 需要全部相匹配才可有,因为这个问题半夜1点查问题 o(╥﹏╥)o,不过如果是要判断字符串的 contains 这个方法就可以使用了,因为他可以判断 "12" contains "1",只不过 Set contains 本场景不适用
+ hour=1
+ validHour=('6' '12' '18')
+ [[ 6 12 18 =~ 1 ]]
+ echo 1 Exist
1 Exist
正确操作 ✅ :
如果想用这个简洁的方法,也可以增加判断 $hour -ge 6 $hour -le 18 控制 hour 的取值范围:
hour='1'
validHour=('6' '12' '18')
if [[ "${validHour[@]}" =~ "$hour" ]] && [[ $hour -ge '6' ]] && [[ $hour -le '18' ]];then
echo $hour Exist
else
echo $hour Not Exist
fi
+ hour=1
+ validHour=('6' '12' '18')
+ validMinute=('0' '10' '20' '30' '40')
+ [[ 6 12 18 =~ 1 ]]
+ [[ 1 -ge 6 ]]
+ echo 1 Not Exist
1 Not Exist
正则操作 ⚠️ :
=~ 常用于正则匹配,例如最常见的判断变量是否为整数:
number=-15
# ps:正则表达式^表示起始,$表示结束,?表示0个或者1个
if [[ "$number" =~ ^-?[0-9]+$ ]]; then
echo "number is an integer."
else
echo "number is not an integer."
exit 1
fi
+ number=-15
+ [[ -15 =~ ^-?[0-9]+$ ]]
+ echo 'number is an integer.'
number is an integer.
四.总结
大半夜修 bug 顺便把最近遇到的相关 shell 坑总结在了一起,希望大家可以少走弯路 ^_^ ,对于本文中使用的 $hour,由于机器的异常原因可能并不会整点启动,所以还需要做规范化,可以参考hour 规范化 ~