關於Linux Shell的訊號trap功能你必須知道的細節
From: http://blog.robotshell.org/2012/necessary-details-about-signal-trap-in-shell/
訊號處理(Signal Handling)在 Linux 程式設計中一直扮演者重要的角色,幾乎每個系統工具都要用到它,最常見的功能莫過於用訊號進行程式間通訊(尤其是父子程式)以及捕捉SIGINT、 SIGTERM之類的退出訊號以做一些善後處理(cleanup)。C中自不必多說,可以使用 wait 族函式;而 shell 指令碼中也有捕捉訊號的 trap 功能——然而許多人在使用 trap 功能的時候卻存在著這樣那樣的誤解,這些看似無關緊要的小細節最後有可能使得你的指令碼與你預想的行為完全不同。
如無特殊說明,下文所指 shell 均以 Bash 為例。
0. trap 的使用簡介
雖然我很想說這些應當要自己看 manpage ,但考慮到也許正在讀文章的你手邊沒有 Linux ,還是簡單說一下吧。
1 | USAGE: trap [action condition ...] |
即當捕捉到 condition 列表所對應的任何一個訊號時,執行 action 動作(使用 eval action 來執行,故 action 可以是 shell 內建指令、外部命令及指令碼中的函式等)。action 還可是”"(空)、’-'等,分別代表忽略相應訊號及重置相應訊號為預設行為。
1. condition 的標準格式是什麼?
condition 中的訊號到底應該如何書寫?比如終端中斷訊號(一般用 CTRL-C 發出),到底是寫 SIGINT 、 INT 還是2(大部分系統上該訊號對應的訊號數)?是大寫還是小寫?
如果你使用最新版的 Bash ,那麼這幾種寫法都可以。而如果你需要在不同 shell 中保持可移植性,請使用大寫、不帶字首的 INT !根據 POSIX 標準, trap 的 condition 不應當加上 SIG 字首,且必須全大寫,允許帶 SIG 字首或小寫是某些 shell 的擴充套件功能。而訊號數在不同的系統上可能不同,所以也不是一個好主意。
2. trap 必須放在第一行麼?
許多資料,尤其是中文資料中不容申辯地指明—— trap 必須放在指令碼中第一個非註釋行。事實果真如此麼?
不論是 manpage 還是 POSIX 文件中,我都沒有找到任何與之相關的說明。甚至在 TLDP 的 中,多個例子都分明把 trap 放在了指令碼的中間。最後我在中找到了下面這句經常被誤讀的話:
Normally, all traps are set before other executable code in the shell script. is encountered, i.e., at the beginning of the shell script.
果然,這只是一個為了保證訊號鉤子儘早被設立的一個設計慣例罷了。事實上, trap 可以根據你的需要放在指令碼中的任何位置。指令碼中也可以有多個 trap ,可以為不同的訊號定義不同的行為,或是修改、刪除已定義的 trap 。更進一步地, trap 也有作用範圍,你可以把它放在函式中,它將只在這個函式里起效!你看,其實 trap 的行為是很符合 UNIX 的慣例的。
3. 訊號究竟是在什麼時候被 trap 處理?
這是本文最重要的一點。訊號到底是什麼時候被處理的?更準確地說,比如指令碼正在執行某個命令時收到了某個訊號,那麼它會被立即處理,還是要等待當前命令完成?
我不打算直接說明答案。為了讓我們對這個問題有更透徹的理解,讓我們來做一下實驗。看下面這個時常被用來講解 trap 的指令碼:
1 2 3 | #!/bin/bash trap 'echo "INTERRUPTED!"; exit' INT sleep 100 |
大多數教程都是這麼做的,執行這個指令碼,按下 CTRL-C 。你看到了什麼?指令碼打出了“INTERRUPTED!”並停止了執行。這看起來似乎很正常、很直覺——以此看來, trap 會立即捕捉到訊號並執行,不管當前正在執行的命令。許多指令碼也正是在這個假設下寫的。
然而真的是這樣麼?讓我們做另一個實驗——在一個終端執行這個指令碼,並開啟另一個終端,用ps -ef|grep bash找到這個指令碼的程式號,然後用kill -SIGINT pid向這個程式傳送 SIGINT 訊號。你在原先的終端中看到了什麼?沒有任何反應!如果你願意等上100秒,你最終會看到“INTERRUPTED!”被輸出。這樣看來 trap 是等到當前命令結束以後再處理訊號。
這樣的矛盾究竟是為什麼?問題其實出在 CTRL-C 身上。 Bash 等終端的預設行為是這樣的:當按下 CTRL-C 之後,它會向當前的整個程式組發出 SIGINT 訊號。而 sleep 是由當前指令碼呼叫的,是這個指令碼的子程式,預設是在同一個程式組的,所以也會收到 SIGINT 並停止執行,返回主程式以後 trap 捕捉到了訊號。
給了我們一個更準確的說明——如果當前正有一個外部命令在前臺執行,那麼 trap 會等待當前命令結束以後再處理訊號佇列中的訊號。(而許多教程出錯的另一個原因就是——某些 shell 中 sleep 是內建命令,會被打斷。)
那麼上文的例子應當要如何寫才能達到想要的效果呢?有兩種方法:一、把 sleep 放到後臺進行,再用內建的 wait 去等待其執行結束(詳見上一段提到的那篇文件);二、暴力一點,把一長段 sleep 拆成一秒的小 sleep 的迴圈,這在對精度要求不高的情況下也是一個可行的辦法(這應該不用寫範例了吧?)來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/27042095/viewspace-768698/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Linux Shell的訊號trap功能細節Linux
- shell訊號捕捉命令 trap
- 關於Http協議,你必須要知道的HTTP協議
- 關於NoSQL,你必須知道的九件事SQL
- 關於Linux作業系統,這些你必須知道!Linux作業系統
- 關於JVM,你必須知道的這些知識點JVM
- 關於 PHP 7 你必須知道的五件事PHP
- 關於索引必須知道的知識索引
- 你必須知道的 SmartSql !SQL
- 29 個你必須知道的 Linux 命令Linux
- 關於LLVM,這些東西你必須知道!LVM
- 關於ES模組你必須要知道的一些禁忌(一)
- 關於Mysql事務,你必須知道的幾個知識點!MySql
- 關於Flutter 您必須知道的知識點!!!Flutter
- 關於時序資料庫,你必須要知道的那些事兒!資料庫
- ?你必須知道的Java泛型Java泛型
- 04.關於執行緒你必須知道的8個問題(下)執行緒
- 03.關於執行緒你必須知道的8個問題(中)執行緒
- 你必須知道的HTTP基本概念HTTP
- 你必須要知道的babel二三事Babel
- 你必須知道的cookie攻防技術!!!Cookie
- 程式猿必須知道的關於 Tomcat 的知識點Tomcat
- 關於汽車一些您必須知道的知識
- 關於響應式佈局,你必須要知道關於響應式佈局的幾件事
- 開發 web 桌面類程式幾個必須關注的細節Web
- Linux新手大禮包:學習Linux你必須知道的那些事兒Linux
- C#.NET 中你必須知道的反射C#反射
- 函式你必須知道的三種角色?函式
- is 和 as 讀你必須知道的.net 筆記筆記
- 你必須要知道的HTTP協議原理HTTP協議
- 敲黑板:InnoDB的Double Write,你必須知道
- 你必須知道的Java基礎知識Java
- 3款你必須知道的爬蟲工具爬蟲
- 你必須知道的基礎演算法演算法
- Python你必須知道的十個庫Python
- .Net Core 你必須知道的source-generators
- 關於機器學習你必須瞭解的十個真相機器學習
- using 你知道多少?你必須知道的.NET讀書筆記筆記