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的簡單介紹到此爲止。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章