首先我们先来看一段代码。
#!/bin/bash declare -i uphosts=0 declare -i downhosts=0 for i in 192.168.152.{98..102}; do if ping -W 2 -c 1 $i &>/dev/null; then echo "$i is up." let uphosts++ else echo "$i is down." let downhosts++ fi done echo "Up hosts is $uphosts, down hosts is $downhosts."
该代码针对一个IP地址段进行ping测试,输出IP地址在线与否并做统计。为了便于观察效果,我们选择的IP地址段较短,从192.168.152.98~102,其中只有主机地址为100(即本机)的主机在线。我们运行一遍看结果。
[root@c7-server ~]# bash trap1.sh 192.168.152.98 is down. 192.168.152.99 is down. 192.168.152.100 is up. 192.168.152.101 is down. 192.168.152.102 is down. Up hosts is 1, down hosts is 4.
接下来我们运行第二遍,在运行的过程中,我们使用Ctrl+c尝试终止脚本的运行。
[root@c7-server ~]# bash trap1.sh 192.168.152.98 is down. ^C192.168.152.99 is down. 192.168.152.100 is up. 192.168.152.101 is down. ^C192.168.152.102 is down. Up hosts is 1, down hosts is 4.
当98和101主机地址结果显示出来以后我们立即键入Ctrl+c(在上面的输出结果上我对其进行了加粗标红),键入后99和102的输出结果立即显示出来了(由于这是代码块展示,因此无法显示出动态效果,大家看我描述脑补或者自行实验)。
我们可以发现Ctrl+c并没有终止脚本的运行,而仅仅只是终止了当前循环中的ping命令的执行。
如果我们想让脚本本身停止,就需要使用bash内置命令trap来捕获我们对脚本发出的Ctrl+c命令。
在我之前的一篇博客中《CentOS 7上的进程管理》已经有谈到通过Ctrl+c命令结束正在执行中的脚本实际上是向脚本进程发出了SIGINT信号。
因此我们需要使用trap捕获SIGINT信号并对其作出反应(在这里是尝试结束脚本的执行)。
trap语法如下。
trap [-lp] [[arg] signal_spec ...]
arg:当接受到指定的信号以后将要执行的命令;
signal_spec:具体的信号;
-l:列出所有的信号,类似于“kill -l”;
-p:打印出目前已经设置的陷阱,仅键入trap命令也有该效果。
[root@c7-server ~]# trap trap -- '' SIGTSTP trap -- '' SIGTTIN trap -- '' SIGTTOU [root@c7-server ~]# trap -p trap -- '' SIGTSTP trap -- '' SIGTTIN trap -- '' SIGTTOU
因此我们只需要在原本的脚本的顶部(shebang下面)写入这样的陷阱即可。
[root@c7-server ~]# cat trap1.sh #!/bin/bash trap "exit" SIGINT declare -i uphosts=0 declare -i downhosts=0 ... ...
结果如下,当脚本一收到SIGINT信号,立即结束脚本,即便循环还未停止。
[root@c7-server ~]# bash trap1.sh 192.168.152.98 is down. ^C[root@c7-server ~]# [root@c7-server ~]#
我们再来看一个更有趣的例子。
假设存在一个脚本,该脚本在运行的过程中会创建各种临时文件,临时文件的路径被保留,脚本执行完毕后自动删除临时文件。为了使得脚本执行遇到中断时,临时文件也可以被清除,我们可以引入trap。注意,这个案例中trap的arg也可以是一个函数。
#!/bin/bash declare -a hosttmpfiles trap "mytrap" INT mytrap() { echo "Quit" rm -f ${hosttmpfiles[@]} exit 1 } for i in 192.168.152.{98..102}; do tmpfile=$(mktemp /tmp/ping.XXXX) if ping -W 1 -c 1 $i &> /dev/null; then echo "$i is up." | tee $tmpfile else echo "$i is down." | tee $tmpfile fi hosttmpfiles[${#hosttmpfiles[*]}]=$tmpfile done rm -f ${hosttmpfiles[@]}
但是这个脚本有一个缺陷,在某些时刻我们键入Ctrl+c时,最新创建的临时文件路径还未进入数组变量hosttmpfiles,这会导致在删除的时候遗漏一个临时文件。
对于信号捕捉(陷阱)trap的简单介绍到此为止。