如果你想做一个网站,二八杠规则:首先需要购买一个域名,与其说是购买不如说是租用,因为你要想使用这个域名,每年都需要缴纳一笔费用。既然是租用,所以就有期限,在到期前没有续费,则该域名会被收回,其他人可以注册并使用这个域名。

当我们名下域名非常多的时候,则很容易忘记为这些域名续费从而导致过期并回收。

本案例的需求是,写一个shell脚本来监控指定的域名是否到期,具体要求如下:

1)写一个函数,域名以参数的形式传递给这个函数

2)域名到期前的一周和到期后的一周(两周时间),每天都要发告警邮件

3)脚本每天执行一次


知识点一:whois

一个域名的信息,比如所有者邮箱、电话、地址和什么时候过期等都是公开的,可以在浏览器里访问http://www.ib911.com/73进行查询。国内也有不少类似的网站可以查询域名信息。在Linux命令行下如何查询呢?

#?whois?aminglinux.com
???Domain?Name:?AMINGLINUX.COM
???Registry?Domain?ID:?1800256822_DOMAIN_COM-VRSN
???Registrar?WHOIS?Server:?whois.55hl.com
???Registrar?URL:?http://www.ib911.com
???Updated?Date:?2018-05-04T22:57:37Z
???Creation?Date:?2013-05-10T06:02:05Z
???Registry?Expiry?Date:?2021-05-10T06:02:05Z
???Registrar:?Jiangsu?Bangning?Science?&?technology?Co.?Ltd.
???Registrar?IANA?ID:?1469
???Registrar?Abuse?Contact?Email:?abuse@55hl.com
???Registrar?Abuse?Contact?Phone:?+86?025?86883426?1009
???Domain?Status:?ok?http://www.149.ib700.com/epp#ok
???Name?Server:?F1G1NS1.DNSPOD.NET
???Name?Server:?F1G1NS2.DNSPOD.NET
???DNSSEC:?unsigned
???URL?of?the?ICANN?Whois?Inaccuracy?Complaint?Form:?http://www.ib808.com/94/wicf/
>>>?Last?update?of?whois?database:?2019-07-25T12:40:54Z?<<<

系统默认没有这个命令,需要安装whois包。

本案例需要监控域名的过期时间,所以要关注的行是Registry Expiry Date。还有一个问题不得不考虑,不同的域(.com,.cn)查询到的结果有所不同,比如.cn的结果是这样的:

#?whois?aminglinux.cn
Domain?Name:?aminglinux.cn
ROID:?20160322s10001s82727381-cn
Domain?Status:?ok
Registrant?ID:?mr2272c32qw63z
Registrant:?个人用户名
Registrant?Contact?Email:?306798658@qq.com
Sponsoring?Registrar:?北京新网数码信息技术有限公司
Name?Server:?ns11.xincache.com
Name?Server:?ns12.xincache.com
Registration?Time:?2016-03-22?17:42:01
Expiration?Time:?2020-03-22?17:42:01
DNSSEC:?unsigned

所以.cn的过期时间需要关注的行是Expiration Time。


知识点二:cut命令

语法:cut -d '分隔字符' [-cf] n 这个n是数字

-d:后面指定分隔字符,分隔字符要用单引号括起来

-c:后面指定第几个字符

-f:后面指定第几个区块

#?cat?/etc/passwd?|cut?-d?':'?-f?1?|head?-n5
root
bin
daemon
adm
lp

说明:-d后面指定冒号为分隔字符,-f 1表示截取第一段,-f和1之间的空格可有可无。

#?head?-n2?/etc/passwd?|cut?-c2
o
i
#?head?-n2?/etc/passwd?|cut?-c1
r
b
#?head?-n2?/etc/passwd?|cut?-c1-10
root:x:0:0
bin:x:1:1:
#?head?-n2?/etc/passwd?|cut?-c5-10
:x:0:0
x:1:1:

说明:-c后面可以是1个数字n,也可以是一个区间n1-n2,还可以是多个数字n1,n2,n3。

#?head?-n2?/etc/passwd?|cut?-c1,3,10
ro0
bn:


知识点三:进程控制

当运行一个进程时,可以按ctrl+z使它暂停,然后使用fg命令恢复它,利用bg命令使它到后台运行,也可以按ctrl+c终止这个进程。输入jobs命令,可以看到暂停或者在后台运行的任务。

如果想把暂停的任务丢在后台跑起来,就使用bg命令。

#?bg?
[1]+?vi?test1.txt?&
[1]+?Stopped????????vi?test1.txt

但是vi并不支持在后台运行,换一个其他的命令:

#?sar?1?>?/tmp/1.log
^Z????//这里按了ctrl+z
[2]+??Stopped????????????sar?1?>?/tmp/1.log
#?jobs
[1]-??Stopped????????????vi?test.txt
[2]+??Stopped????????????sar?1?>?/tmp/1.log
#?bg?2
[2]+??sar?1?>?/tmp/1.log?&

说明:多个被暂停的任务会有编号,使用jobs命令可以看到两个任务,使用bg或者fg的时候,就需要在后面加一个编号。上例中使用bg 2把第二个被暂停的任务丢到后台跑起来了,命令行下使用命令在最后面加一个&符号,可以将这条命令直接丢入后台。

丢到后台的任务如何关闭?

如果没有退出刚才的shell,那么,先使用“fg 编号”把任务调到前台,再使用“ctrl+c”结束任务:

#?fg?2
sar?1?>?/tmp/1.log
^C???//这里使用了ctrl+c

另一种情况则是,关闭掉了当前的shell,再次打开一个新的shell中时,使用jobs命令并不会显示在后台运行或者被暂停的任务,要想停掉它的话,则需要先知道其pid,然后使用kill命令杀死那个进程。

#?sar?1?10?>?/tmp/1.log?&
[1]?30218
#?ps?aux?|grep?sar
root??????30218??0.0??0.0?108036???760?pts/0????S????11:22???0:00?sar?1?10
root??????30221??0.0??0.0?112724???984?pts/0????S+???11:22???0:00?grep?--color=auto?sar

在shell脚本中,多条指令执行是有先后顺序的,就是说只有前面的指令执行完(不管成功与否),后面的指令才会执行。如果有一条指令运行时间比较久,则会阻碍后面的指令执行。若不想让这条执行慢的指令影响后面的指令,在执行该指令时,在后面加一个&,把它丢到后台去。使用&符号把任务丢到后台运行,会显示pid信息,如果忘记这个pid,我们还可以使用ps aux命令找到那个进程。想结束掉该进程,需要使用kill命令:

#?kill?9433
[1]+?已终止?????sar?1?>?/tmp/1.log

kill命令语法很简单,直接在后面加pid即可。


知识点四:判断一个变量是否为空

在shell脚本中,如果一个变量没有成功赋值就被引用了,则会影响到脚本的正常执行,判断一个变量的值是否为空有两种方法:

1)用-z(zero的意思,为空)

#?b=???????//给变量b赋值为空
#?if?[?-z?"$b"?];?then?echo?"The?value?of?b?is?null.";else?echo?"The?value?of?b?is?$b";fi
The?value?of?b?is?null.
#?b=1
#?if?[?-z?"$b"?];?then?echo?"The?value?of?b?is?null.";else?echo?"The?value?of?b?is?$b.";fi
The?value?of?b?is?1.

2)用-n(not null的意思,不为空)

#?a=1
#?if?[?-n?"$a"?];?then?echo?"The?value?of?a?is?$a.";else?echo?"The?value?of?a?is?null.";fi
The?value?of?a?is?1.
#?a=?
#?if?[?-n?"$a"?];?then?echo?"The?value?of?a?is?$a.";else?echo?"The?value?of?a?is?null.";fi
The?value?of?a?is?null.


知识点五:判断某个进程是否存在

之前有用到过ps查看进程,但是需要结合grep,而且需要统计行数,其实还有一个更简单的用法:

#?sleep?100?&
[1]??32744
#?pgrep?sleep
32744


知识点六:杀死进程

前面有用到kill命令杀死进程,但是需要知道进程的pid。再介绍一个简单的用法:

#?sar?1?>?/tmp/1.log?&
#?killall?sar
[1]+??已终止?????sar?1?>?/tmp/sar.log

说明:killall和kill不同的地方在于killall可以直接跟进程名。killall也支持-9选项,有时候用killall杀死进程不好用,需要带上-9,但是进程杀不死时,要慎用-9。


本案例参考脚本

#!/bin/bash
#检测域名是否过期
#作者:
#日期:
#版本:v0.2

mail_u=admin@admin.com
#当前日期时间戳,用于和域名的到期时间做比较
t1=`date?+%s`

#检测whois命令是否存在,不存在则安装whois包
is_install_whois()
{
????which?whois?>/dev/null?2>/dev/null
????if?[?$??-ne?0?]
????then
????????yum?install?-y?whois
????fi
}

notify()
{
????e_d=`whois?$1?|grep?'Expiry?Date'?|awk?'{print?$4}'?|cut?-d?'T'?-f?1`
????#上面的$1代表域名,遍历循环出来的。
????#如果e_d的值为空,则过滤关键词'Expiration?Time'
????if?[?-z?"$e_d"?]
????then
????????e_d=`whois?$1|grep?'Expiration?Time'?|awk?'{print?$3}'`
????fi
????#将域名过期的日期转化为时间戳???
????e_t=`date?-d?"$e_d"?+%s`
????#计算一周一共有多少秒
????n=`echo?"86400*7"?|bc`
????e_t1=$[$e_t-$n]?#过期时间一周前的时间戳
????e_t2=$[$e_t+$n]?#过期时间一周后的时间戳
????if?[?$t1?-ge?$e_t1?]?&&?[?$t1?-lt?$e_t?]
????then
????????python?mail.py?$mail_u?"Domain?$1?will?to?be?expired."?"Domain?$1?expire?date?is?$e_d."
????fi
????if?[?$t1?-ge?$e_t?]?&&?[?$t1?-lt?$e_t2?]
????then
????????python?mail.py?$mail_u?"Domain?$1?has?been?expired."?"Domain?$1?expire?date?is?$e_d."
????fi????
}

#检测上次运行的whois查询进程是否存在
#若存在,需要杀死进程,以免影响本次脚本执行
if?pgrep?whois?&>/dev/null
then
????killall?-9?whois
fi
????
is_install_whois

for?d?in?aaa.net?aaa.com?bbb.com?aaa.cn?ccc.com
do
??notify?$d?&
done