何时使用set -e

时间:2012-11-20 07:32:52

标签: bash shell

前一段时间我遇到set -e,我承认我喜欢它。 现在,经过一段时间我回来写一些bash脚本。

我的问题是,是否有一些最佳做法何时使用set -e以及何时不使用它(.eg在小/大脚本中等)或者我应该使用像cmd || exit 1这样的模式跟踪错误?

4 个答案:

答案 0 :(得分:58)

是的,你应该经常使用它。人们总是取笑Visual Basic,说它不是真正的编程语言,部分是因为它的“On Error Resume Next”声明。然而这是shell中的默认值! set -e应该是默认值。灾难的可能性太大了。


在命令失败的地方,您可以使用|| true或缩写形式||:,例如

grep Warning build.log ||:

事实上你应该更进一步,并且

set -eu
set -o pipefail

位于每个bash脚本的顶部。

-u使引用不存在的环境变量(例如${HSOTNAME})成为错误,代价是在您引用${#}之前需要一些体操检查${1}${2},等等。

pipefail会使misspeled-command | sed -e 's/^WARNING: //'之类的内容产生错误。

答案 1 :(得分:3)

如果您的脚本代码在必要时仔细并正确地检查错误,并以适当的方式处理它们,那么您可能根本不需要或不想使用set -e

另一方面,如果您的脚本是一个接一个地运行的命令的简单顺序列表,并且如果您希望脚本在其中任何一个失败时终止,那么将set -e放在顶部会正是你想要做的事情,以保持你的脚本简单和整洁。一个完美的例子就是如果您正在创建一个脚本来编译一组源代码,并且您希望在遇到第一个有错误的文件后停止编译。

更复杂的脚本可以组合这些方法,因为您可以使用set +e再次关闭其效果并返回显式错误检查。

请注意,虽然set -e应该导致shell退出 IFF ,但任何未经测试的命令都会失败,但是当你的代码再次关闭它时是明智的正在进行自己的错误处理,因为很容易出现奇怪的情况,即命令将返回一个你不期望的非零退出状态,甚至可能发生你可能没有在测试中遇到的情况,以及突然致命终止你的脚本会让事情处于糟糕状态。因此,请不要使用set -e,或在短暂使用后将其保持开启状态,除非您确实知道自己需要它。

另请注意,您仍然可以使用trap ERR定义错误处理程序,以便在set -e生效时执行错误操作,因为在shell退出之前仍会运行。

答案 2 :(得分: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或者您的..

答案 3 :(得分:0)

当此选项打开时,如果简单命令因Shell错误的后果中列出的任何原因而失败,或者返回退出状态值> 0,并且在一段时间之后不是复合列表的一部分,直到或if关键字,并且不是AND或OR列表的一部分,并且不是以!开头的管道!保留字,然后贝壳应立即 退出。