shell中trap的使用


項目中的升級腳本可能耗時很長,在這段時間內,腳本沒有任何輸出的,這帶給市場部署人員的感覺就是腳本好像卡住了。通常情況下,部署人員都會直接CTRL+C停掉升級腳本,這會導致升級失敗,最終需要開發人員介入去修復環境。

可以通過輸出升級進度的方式提示部署人員升級正在進行中,但進度也可能在一段時間不動,而且無法避免意外終止升級的情況,此時可以使用Shell的內建命令trap來忽略SIGINT這些信號,保證升級不會中斷。

trap介紹

trap的格式如下,功能就是設置信號處理行為

trap [-lp] [[arg] sigspec ...] 
  • arg可以是shell命令或者自定義函數
  • sigspec可以是以下的一個或多個
    • 定義在<signal.h>中的信號名或者數值。信號名的大小寫不敏感,SIG這個前綴也是可選的。以下的命令的效果都是一樣的
  1.  
    trap "echo 123" SIGINT
  2.  
    trap "echo 123" INT
  3.  
    trap "echo 123" 2
  4.  
    trap "echo 123" int
  5.  
    trap "echo 123" Int
    • EXIT:在shell退出前執行trap設置的命令,也可以指定為0
    • RETURN:在函數返回時,或者.source執行其他腳本返回時,執行trap設置的命令
    • DEBUG:在任何命令執行前執行trap設置的命令,但對於函數僅在函數的第一條命令前執行一次
  1.  
    #!/bin/bash
  2.  
    foo()
  3.  
    {
  4.  
    echo "foo 1"
  5.  
    echo "foo 2"
  6.  
    }
  7.  
     
  8.  
    trap "echo 123" DEBUG
  9.  
    foo

執行結果

  1.  
    123 # 在函數前執行一次
  2.  
    foo 1
  3.  
    foo 2
  • ERR:在命令結果為非0時,執行trap設置的命令

常用方式

  • trap -l:列出所有信號的數值和名字,類似於kill -l
  1.  
    [ root@localhost ~]# trap -l
  2.  
    1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
  3.  
    6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
  4.  
    11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
  5.  
    16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
  6.  
    21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
  7.  
    26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
  8.  
    31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
  9.  
    38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
  10.  
    43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
  11.  
    48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
  12.  
    53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
  13.  
    58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
  14.  
    63) SIGRTMAX-1 64) SIGRTMAX
  • trap -p:列出通過trap設置的信號處理命令。
  1.  
    [root@localhost ~] # trap "echo INT" INT
  2.  
    [root@localhost ~] # trap -p INT
  3.  
    trap -- 'echo INT' SIGINT
  • trap "" sigspec:忽略sigspec指定的信號
  • trap "do something" sigspec:收到sigspec指定的信號時,do some thing后,繼續執行后續命令。
  • trap sigspec or trap - sigspec:恢復sigspec指定的信號的默認行為

trap的注意事項

  • trap可以在收到信號前的任意位置設置,並非需要在腳本的第一行,但是shell是按照順序執行語句的,不會優先執行trap
  1.  
    #!/bin/bash
  2.  
    trap -p INT # 不輸出任何信息
  3.  
    trap "echo get signal" INT
  • 在函數中設置trap,也是全局生效的
  1.  
    #!/bin/bash
  2.  
    foo()
  3.  
    {
  4.  
    trap "echo get signal" INT
  5.  
    }
  6.  
    foo
  7.  
    trap -p INT # 輸出trap -- 'echo get signal' SIGINT
  • 對於同一個信號,只有最后一次trap生效
  • trap只在本進程內有效,它的子進程不會繼承trap的設置。
    main.sh
  1.  
    #!/bin/bash
  2.  
    trap "get signal" INT
  3.  
    ./sub.sh

sub.sh

  1.  
    #!/bin/bash
  2.  
    trap -p INT # 沒有任何信息
  • 如果子進程阻塞着,當通過kill直接殺死父進程時,只有等到子進程退出,父進程才會處理信號。kill -2 殺掉以下腳本的進程,此時需要等待10秒后,才會輸出"get signal"。因為CTRL+C的信號是發送給進程組,此時sleep進程被INT信號中斷了,所以立即輸出了"get signal",可以用Kill -2 發送信號到進程組達到一樣的效果。
  1.  
    #!/bin/bash
  2.  
    trap "get signal" INT
  3.  
    sleep 10

還有一個變通的方法就是把sleep放在后台進行,並用wait等待,wait是shell的內建命令,會被本進程收到的信號直接打斷,此時sleep是繼續在后台執行的。

  1.  
    #!/bin/bash
  2.  
    trap "get signal" INT
  3.  
    sleep 10 & wait $!
  • 處理SIGINT或者SIGQUIT時,需要特別注意。比如下面的腳本,CTRL+C后只是中斷了一次sleep,當信號處理結束后,又會進入下一次sleep,這可能並不符合預期。
  1.  
    #!/bin/bash
  2.  
    trap "echo get signal" INT
  3.  
    sleep 10
  4.  
    sleep 10

需要在處理信號中,將信號處理恢復到默認,並以INT信號再次殺掉自己

  1.  
    #!/bin/bash
  2.  
    trap "echo get signal;trap - INT;kill -s INT "$$" " INT
  3.  
    sleep 10
  4.  
    sleep 10

參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM