33

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

我的问题是是否有一些最佳实践何时使用set -e和何时不使用它(例如在小/大脚本等中),还是我应该使用像cmd || exit 1跟踪错误这样的模式?

4

4 回答 4

77

是的,您应该始终使用它。人们总是取笑 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: //'引发错误之类的事情。

于 2012-11-20T17:23:09.150 回答
5

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

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

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

请注意,虽然set -e应该导致 shell 退出IFF任何未经测试的命令失败,但明智的做法是在您的代码进行自己的错误处理时再次将其关闭,因为很容易出现命令返回非零的奇怪情况您没有预料到的退出状态,甚至可能是您在测试中可能无法捕捉到的情况,以及脚本的突然致命终止会使某些东西处于不良状态。因此,请勿使用set -e,或在短暂使用后将其打开,除非您真的知道自己想要它。

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

于 2012-11-20T11:02:22.047 回答
4

喜欢它!?

对于我自己,我更喜欢宽泛的,.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或者您的..

于 2012-11-20T16:51:17.973 回答
0

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

于 2012-11-20T07:37:53.010 回答