Bash脚本编程学习笔记09:信号捕捉

简介

首先我们先来看一段代码。

#!/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的简单介绍到此为止。

原文地址:https://www.cnblogs.com/alongdidi/p/bash_trap.html