我有些时间前接触到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退出,但当您的代码进行自己的错误处理时最好将其关闭,因为可能会出现意外情况,即一个命令将返回一个您没有预料到的非零退出状态,甚至可能在测试中无法捕获这种情况,而突然的致命终止将使您的脚本处于糟糕的状态。因此,除非您确实知道需要它,否则不要使用set -e
或者在短暂使用后将其保持打开状态。set -e
的情况下仍然可以定义错误处理程序trap ERR
,以在发生错误条件时执行某些操作,因为该处理程序仍将在退出shell之前运行。set -e
很少有用。 - Greg A. Woods&&
,||
)的结果,或在流程控制语句(if
,while
)的条件项中。 - Greg A. Woods您喜欢这个吗?
对我而言,我更喜欢在我的.bashrc
中添加以下行:
trap '/usr/games/fortune /usr/share/games/fortunes/bofh-excuses' ERR
(在Debian系统中安装BOFH借口的方法: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
或者您的其他环境变量。
/tmp
!这可能会成为安全漏洞!也许使用另一个地方,如$HOME/.bash_errors
将是更合适的选择! - F. Hauri - Give Up GitHub当此选项打开时,如果简单命令由于 Shell 错误的任何原因之一而失败或返回一个大于 0 的退出状态值,并且不是 while、until 或 if 关键字后面的复合列表的一部分,也不是 AND 或 OR 列表的一部分,也不是由 ! 保留字前导的管道,则 shell 将立即退出。