項目中的升級腳本可能耗時很長,在這段時間內,腳本沒有任何輸出的,這帶給市場部署人員的感覺就是腳本好像卡住了。通常情況下,部署人員都會直接CTRL+C停掉升級腳本,這會導致升級失敗,最終需要開發人員介入去修復環境。
可以通過輸出升級進度的方式提示部署人員升級正在進行中,但進度也可能在一段時間不動,而且無法避免意外終止升級的情況,此時可以使用Shell的內建命令trap來忽略SIGINT這些信號,保證升級不會中斷。
trap介紹
trap的格式如下,功能就是設置信號處理行為
trap [-lp] [[arg] sigspec ...]
arg
可以是shell命令或者自定義函數sigspec
可以是以下的一個或多個-
- 定義在<signal.h>中的信號名或者數值。信號名的大小寫不敏感,SIG這個前綴也是可選的。以下的命令的效果都是一樣的
-
trap "echo 123" SIGINT
-
trap "echo 123" INT
-
trap "echo 123" 2
-
trap "echo 123" int
-
trap "echo 123" Int
-
- EXIT:在shell退出前執行trap設置的命令,也可以指定為0
-
- RETURN:在函數返回時,或者
.
和source
執行其他腳本返回時,執行trap設置的命令
- RETURN:在函數返回時,或者
-
- DEBUG:在任何命令執行前執行trap設置的命令,但對於函數僅在函數的第一條命令前執行一次
-
-
foo()
-
{
-
echo "foo 1"
-
echo "foo 2"
-
}
-
-
trap "echo 123" DEBUG
-
foo
執行結果
-
123 # 在函數前執行一次
-
foo 1
-
foo 2
- ERR:在命令結果為非0時,執行trap設置的命令
常用方式
trap -l
:列出所有信號的數值和名字,類似於kill -l
-
[ root@localhost ~]# trap -l
-
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
-
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
-
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
-
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
-
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
-
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
-
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
-
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
-
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
-
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
-
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
-
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
-
63) SIGRTMAX-1 64) SIGRTMAX
trap -p
:列出通過trap設置的信號處理命令。
-
[root@localhost ~] # trap "echo INT" INT
-
[root@localhost ~] # trap -p INT
-
trap -- 'echo INT' SIGINT
trap "" sigspec
:忽略sigspec指定的信號trap "do something" sigspec
:收到sigspec指定的信號時,do some thing后,繼續執行后續命令。trap sigspec
ortrap - sigspec
:恢復sigspec指定的信號的默認行為
trap的注意事項
- trap可以在收到信號前的任意位置設置,並非需要在腳本的第一行,但是shell是按照順序執行語句的,不會優先執行trap
-
-
trap -p INT # 不輸出任何信息
-
trap "echo get signal" INT
- 在函數中設置trap,也是全局生效的
-
-
foo()
-
{
-
trap "echo get signal" INT
-
}
-
foo
-
trap -p INT # 輸出trap -- 'echo get signal' SIGINT
- 對於同一個信號,只有最后一次trap生效
- trap只在本進程內有效,它的子進程不會繼承trap的設置。
main.sh
-
-
trap "get signal" INT
-
./sub.sh
sub.sh
-
-
trap -p INT # 沒有任何信息
- 如果子進程阻塞着,當通過kill直接殺死父進程時,只有等到子進程退出,父進程才會處理信號。kill -2 殺掉以下腳本的進程,此時需要等待10秒后,才會輸出"get signal"。因為CTRL+C的信號是發送給進程組,此時sleep進程被INT信號中斷了,所以立即輸出了"get signal",可以用Kill -2 發送信號到進程組達到一樣的效果。
-
-
trap "get signal" INT
-
sleep 10
還有一個變通的方法就是把sleep放在后台進行,並用wait等待,wait是shell的內建命令,會被本進程收到的信號直接打斷,此時sleep是繼續在后台執行的。
-
-
trap "get signal" INT
-
sleep 10 & wait $!
- 處理SIGINT或者SIGQUIT時,需要特別注意。比如下面的腳本,CTRL+C后只是中斷了一次sleep,當信號處理結束后,又會進入下一次sleep,這可能並不符合預期。
-
-
trap "echo get signal" INT
-
sleep 10
-
sleep 10
需要在處理信號中,將信號處理恢復到默認,並以INT信號再次殺掉自己
-
-
trap "echo get signal;trap - INT;kill -s INT "$$" " INT
-
sleep 10
-
sleep 10