bash訊號捕捉

昀溪發表於2019-05-11

我們ping一個主機,然後按下ctrl+c那麼就會終止這個ping動作,如下圖:

可是如果使用一個迴圈來逐個ping不同主機,你再按下ctrl+c就會發現停不下來,直到迴圈完成,如下圖:

#!/bin/bash

NETWORK=172.16.42.

# -W 表示超時時長  -c 是傳送幾個ping包
for IP in {1..20}; do
  ping -W 1 -c 10 ${NETWORK}${IP}
done

指令碼沒有停止而是依然繼續執行,但是你發現172.16.42.1是通的,而且我們透過-c引數應該是ping 10次,當完成第5次ping的時候,我們按下ctrl+c它就不再ping這個地址,而是開始ping 172.16.42.2這個地址。這就是ctrl+c的真正含義,它的作用是終止當前正在執行的操作,指令碼中迴圈20次,每次執行一個ping操作,所以ctrl+c僅僅終止的是其中一個ping操作而不是整個指令碼。

不過這麼解釋並不完全正確,因為你要知道ctrl+c是傳送中斷訊號,到底是應該終止ping操作還是這個指令碼,取決於捕捉到這個中斷訊號的程式,如果是ping捕捉到了,那麼就終止ping操作;如果是執行這個指令碼的程式捕捉到就終止這個指令碼的執行。那麼我們如何設定捕捉一個訊號呢?就使用trap這個內建的shell命令。

trap -l顯示系統訊號[1]kill -l也是可以顯示的。

trap命令不能捕捉SIGKILL和SIGTERM這兩個訊號。捕捉訊號的目的是一旦訊號到達我們針對訊號做什麼處理,如果捕捉SIGKILL並且你修改了行為,這就意味著這個程式刀槍不入了,這顯然不行。一般我們捕捉SIGHUP、SIGINT等。

針對上面的例子如何修改呢?

#!/bin/bash

# 捕捉INT,然後執行exit 1,該命令通常寫在指令碼第一行
trap 'exit 1' INT

NETWORK=172.16.42.
# -W 表示超時時長 -c 是傳送幾個ping包
for IP in {1..20}; do
 ping -W 1 -c 10 ${NETWORK}${IP}
done

再次執行這個指令碼那麼依然會執行迴圈,但是trap並不執行而是一直等著訊號發生,我們使用的ctrl+c其實就是SIGINT訊號。這個指令碼的含義就是shell捕捉訊號,所以shell捕捉到以後就會執行響應動作,我們這裡是trap 'exit 1' INT捕捉SIGINT然後執行exit 1,當shell執行這個命令時也就意味著退出了,所以無論for迴圈是否執行完畢它都隨著指令碼的退出而終止。

如果你想讓捕捉訊號時做更多操作,你可以使用函式的方式,如下程式碼:

#!/bin/bash

trap 'sig_handler' INT
sig_handler(){
    echo "Quit"
    exit 1
}

NETWORK=172.16.42.
# -W 表示超時時長 -c 是傳送幾個ping包
for IP in {1..20}; do
 ping -W 1 -c 10 ${NETWORK}${IP}
done

也就是突然終止後需要做一些收尾的清理操作,你就可以透過上面自定義一個函式來執行。


  1. 訊號是程式間通訊的一種方式 ↩︎

相關文章