簡介
首先我們先來看一段代碼。
#!/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的簡單介紹到此爲止。