set -e
我前段时间遇到过,我承认我喜欢它。现在,过了一段时间我回来写一些 bash 脚本。
我的问题是是否有一些最佳实践何时使用set -e
和何时不使用它(例如在小/大脚本等中),还是我应该使用像cmd || exit 1
跟踪错误这样的模式?
是的,您应该始终使用它。人们总是取笑 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: //'
引发错误之类的事情。
如果您的脚本代码在必要时仔细和正确地检查错误,并以适当的方式处理它们,那么您可能永远不需要或不想使用set -e
.
另一方面,如果您的脚本是一个简单的顺序命令列表,要一个接一个地运行,并且如果您希望脚本在其中任何一个失败时终止,那么坚持set -e
在顶部将正是您想要做的保持你的脚本简单和整洁。一个完美的例子是,如果您正在创建一个脚本来编译一组源代码,并且您希望在遇到第一个有错误的文件后停止编译。
更复杂的脚本可以组合这些方法,因为您可以使用set +e
它来再次关闭其效果并返回显式错误检查。
请注意,虽然set -e
应该导致 shell 退出IFF任何未经测试的命令失败,但明智的做法是在您的代码进行自己的错误处理时再次将其关闭,因为很容易出现命令返回非零的奇怪情况您没有预料到的退出状态,甚至可能是您在测试中可能无法捕捉到的情况,以及脚本的突然致命终止会使某些东西处于不良状态。因此,请勿使用set -e
,或在短暂使用后将其打开,除非您真的知道自己想要它。
另请注意,您仍然可以定义一个错误处理程序,trap ERR
以便在错误条件set -e
生效时执行某些操作,因为它仍然会在 shell 退出之前运行。
你喜欢它!?
对于我自己,我更喜欢宽泛的,.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
或者您的..
当此选项打开时,如果简单命令由于 Shell 错误的后果中列出的任何原因而失败或返回退出状态值 >0,并且在一段时间、直到或 if 关键字之后不属于复合列表的一部分,并且不是 AND 或 OR 列表的一部分,也不是前面有 ! 的管道 保留字,则shell应立即退出。