2012-11-20 26 views
20

我遇到一個set -e前一段時間,我承認我喜歡它。 現在,一段時間後,我回來寫一些bash腳本。時使用設置-e

我的問題是,如果有一些最佳做法何時使用set -e以及何時不使用它(例如在小/大腳本等中),還是應該使用像cmd || exit 1這樣的模式來跟蹤錯誤?

+0

另請參閱服務器故障上的[set -e'是做什麼的,爲什麼它會被認爲是危險的?](http://serverfault.com/q/143445/204345)。特別是[Debian bug](http://www.mail-archive.com/[email protected]/msg473314.html)在我看來是相當棘手的。 – Kontrollfreak

回答

48

是的,你應該總是使用它。人們總是嘲笑Visual Basic,說它不是一種真正的編程語言,部分原因是它的「On Error Resume Next」語句。然而,這是shell的默認值! set -e應該是默認值。災難的可能性太高。


在地方,這是確定的命令失敗,你可以使用|| true或者它的簡寫形式||:,例如

​​3210

其實你應該在每一個bash腳本的頂部向前邁進一步,並有

set -eu 
set -o pipefail 

-u使得錯誤引用一個不存在的環境變量,例如${HSOTNAME},在需要一些體操與檢查${#}的成本你參考${1}${2},等等之前。

pipefail使事情像misspeled-command | sed -e 's/^WARNING: //'提出錯誤。

+0

這裏的每個人都對這個問題有個人喜好。你的答案最接近我,所以我接受它。 – dimba

0

當這個選項是,如果一個簡單的命令用於任何的在殼錯誤的後果列出的原因失敗或返回一個退出狀態值> 0,並且不跟隨,而化合物列表的一部分,直到,或如果是關鍵字,並且不是AND或OR列表的一部分,並且不是以!開頭的管道。保留字,則外殼應立即 退出。

+0

謝謝,但我的問題是什麼時候推薦使用它?例如,我在init腳本中看不到它(/etc/init.d/*) – dimba

+2

創建init.d腳本時使用「set -e」會很危險: 請小心使用set -e在init.d腳本中。當守護進程正在運行或停止而不中止init.d腳本時,init.d腳本需要退出狀態。所以每當你不關心如果發生錯誤時中止你的程序使用設置,否則沒有 –

3

如果你的腳本代碼檢查錯誤小心,正確地在必要時,並處理它們以適當的方式,那麼你可能永遠不會需要或想要使用set -e。另一方面,如果您的腳本是一個接一個地運行的命令的簡單順序列表,並且如果您希望腳本在其中任何一個失敗時終止,那麼在頂部粘貼set -e將恰好是你會想要保持你的腳本簡單而整齊。一個完美的例子是,如果你正在創建一個腳本來編譯一組源代碼,並且希望遇到遇到錯誤的第一個文件後編譯停止。

更復雜的腳本,可以結合這些方法,因爲你可以使用set +e再次後退把它的效果,回去明確的錯誤檢查。

注意,雖然set -e應該導致shell退出IFF任何未經檢驗命令失敗,這是明智的再將它關閉時,你的代碼是做自己的錯誤處理,因爲很容易被怪異的情況下,在那裏一個命令會返回一個你不期望的非零退出狀態,甚至可能出現你在測試中可能無法捕獲的情況,以及突然致命的終止腳本會導致某些狀況不佳的情況。所以,不要使用set -e,或者在短暫使用之後將其打開,除非您確實知道您需要它。

還要注意,當set -e生效時,仍然可以在shell退出之前運行,您仍然可以在trap ERR中定義一個錯誤處理程序,以便在出現錯誤時執行某些操作。

+4

我不同意。我認爲你應該默認使用'set -e',並且只有在你確實需要的時候纔會刪除它。你想要的最後一件事是一個未處理的錯誤,它會導致腳本中出現更多錯誤或某種不正確行爲。 –

+0

@MikeWeller很高興聽到其他意見:) – dimba

+1

25年以上的閱讀和編寫腳本以供生產使用的經驗表明'set -e'很少有用。 –

3

吧!?

對於我自己,我更喜歡在很寬的,有我在.bashrc這樣一行:

trap '/usr/games/fortune /usr/share/games/fortunes/bofh-excuses' ERR 

(Debian的:apt-get install fortunes-bofh-excuses :-)

但這只是我的偏好;-)

更嚴重的是

lastErr() { 
    local RC=$? 
    history 1 | 
     sed ' 
    s/^ *[0-9]\+ *\(\(["'\'']\)\([^\2]*\)\2\|\([^"'\'' ]*\)\) */cmd: \"\3\4\", args: \"/; 
    s/$/", rc: '"$RC/" 
} 
trap "lastErr" ERR 

Gna 
bash: Gna : command not found 
cmd: "Gna", args: "", rc: 127 

Gna gna 
cmd: "Gna", args: "gna", rc: 127 

"Gna gna" foo 
cmd: "Gna gna", args: "foo", rc: 127 

好了,從那裏,你可以:

trap "lastErr >>/tmp/myerrors" ERR 
"Gna gna" foo 

cat /tmp/myerrors 
cmd: "Gna gna", args: "foo", rc: 1 

或者更好:

lastErr() { 
local RC=$? 
history 1 | 
    sed ' 
    s/^ *[0-9]\+ *\(\(["'\'']\)\([^\2]*\)\2\|\([^"'\'' ]*\)\) */cmd: \"\3\4\", args: \"/; 
    s/$/", rc: '"$RC/ 
    s/^/$(date +"%a %d %b %T ")/" 
} 
"Gna gna" foo 

cat /tmp/myerrors 
cmd: "Gna gna", args: "foo", rc: 1 
Tue 20 Nov 18:29:18 cmd: "Gna gna", args: "foo", rc: 127 

...你甚至可以添加其他信息一樣$$, $PPID, $PWD或者你..

+0

+1。非常好的提示。 – ghoti