本文翻译自:What does set -e mean in a bash script?
I'm studying the content of this preinst file that the script executes before that package is unpacked from its Debian archive (.deb) file. 我正在研究脚本在从Debian存档(.deb)文件解压缩该包之前执行的这个preinst文件的内容。
The script has the following code: 该脚本具有以下代码:
#!/bin/bash
set -e
# Automatically added by dh_installinit
if [ "$1" = install ]; then
if [ -d /usr/share/MyApplicationName ]; then
echo "MyApplicationName is just installed"
return 1
fi
rm -Rf $HOME/.config/nautilus-actions/nautilus-actions.conf
rm -Rf $HOME/.local/share/file-manager/actions/*
fi
# End automatically added section
My first query is about the line: 我的第一个问题是关于这一行:
set -e
I think that the rest of the script is pretty simple: It checks whether the Debian/Ubuntu package manager is executing an install operation. 我认为脚本的其余部分非常简单:它检查Debian / Ubuntu包管理器是否正在执行安装操作。 If it is, it checks whether my application has just been installed on the system. 如果是,它会检查我的应用程序是否刚刚安装在系统上。 If it has, the script prints the message "MyApplicationName is just installed" and ends ( return 1
mean that ends with an “error”, doesn't it?). 如果有,脚本将打印消息“MyApplicationName刚刚安装”并结束( return 1
表示以“错误”结束,不是吗?)。
If the user is asking the Debian/Ubuntu package system to install my package, the script also deletes two directories. 如果用户要求Debian / Ubuntu软件包系统安装我的软件包,该脚本还会删除两个目录。
Is this right or am I missing something? 这是对的还是我错过了什么?
#1楼
参考:https://stackoom.com/question/1KKd4/set-e在bash脚本中的含义是什么
#2楼
set -e
stops the execution of a script if a command or pipeline has an error - which is the opposite of the default shell behaviour, which is to ignore errors in scripts. set -e
在命令或管道出错时停止执行脚本 - 这与默认shell行为相反,即忽略脚本中的错误。 Type help set
in a terminal to see the documentation for this built-in command. 在终端中键入help set
以查看此内置命令的文档。
#3楼
From help set
: 来自help set
:
-e Exit immediately if a command exits with a non-zero status.
But it's considered bad practice by some (bash FAQ and irc freenode #bash FAQ authors). 但是一些人认为这是不好的做法(bash FAQ和irc freenode #bash FAQ作者)。 It's recommended to use: 建议使用:
trap 'do_something' ERR
to run do_something
function when errors occur. 发生错误时运行do_something
函数。
See http://mywiki.wooledge.org/BashFAQ/105 见http://mywiki.wooledge.org/BashFAQ/105
#4楼
As per bash - The Set Builtin manual, if -e
/ errexit
is set, shell exits immediately if a pipeline consist of a single simple command , a list or a compound command returns a non-zero status. 根据bash - Set Builtin手册,如果设置了 -e
/ errexit
,如果管道由单个简单命令 , 列表或复合命令返回非零状态,则shell立即退出。
By default, the exit status of a pipeline is the exit status of the last command in the pipeline, unless the pipefail
option is enabled (it's disabled by default). 默认情况下,管道的退出状态是管道中最后一个命令的退出状态,除非启用了pipefail
选项(默认情况下禁用)。
If so, the pipeline's return status of the last (rightmost) command to exit with a non-zero status, or zero if all commands exit successfully. 如果是这样,管道的最后(最右边)命令的返回状态以非零状态退出,或者如果所有命令都成功退出则返回零。
If you'd like to execute something on exit, try defining trap
, for example: 如果您想在退出时执行某些操作,请尝试定义trap
,例如:
trap onexit EXIT
where onexit
is your function to do something on exit, like below which is printing the simple stack trace : 其中onexit
是你在退出时执行某些操作的功能,如下面打印简单的堆栈跟踪 :
onexit(){ while caller $((n++)); do :; done; }
There is similar option -E
/ errtrace
which would trap on ERR instead, eg: 有类似的选项-E
/ errtrace
,它会在ERR上捕获,例如:
trap onerr ERR
Examples 例子
Zero status example: 零状态示例:
$ true; echo $?
0
Non-zero status example: 非零状态示例:
$ false; echo $?
1
Negating status examples: 否定状态示例:
$ ! false; echo $?
0
$ false || true; echo $?
0
Test with pipefail
being disabled: 禁用pipefail
进行测试:
$ bash -c 'set +o pipefail -e; true | true | true; echo success'; echo $?
success
0
$ bash -c 'set +o pipefail -e; false | false | true; echo success'; echo $?
success
0
$ bash -c 'set +o pipefail -e; true | true | false; echo success'; echo $?
1
Test with pipefail
being enabled: 使用pipefail
进行测试:
$ bash -c 'set -o pipefail -e; true | false | true; echo success'; echo $?
1
#5楼
I found this question while Googling, trying to figure out what the exit status was for a script that was aborted due to a set -e
. 我在Googling中找到了这个问题,试图弄清楚由于set -e
而中止的脚本的退出状态。 The answer didn't appear obvious to me; 答案对我来说并不明显; hence this answer. 因此这个答案。 Basically, set -e
aborts the execution of a command (eg a shell script) and returns the exit status code of the command that failed (ie the inner script, not the outer script) . 基本上, set -e
中止命令的执行(例如shell脚本)并返回失败命令的退出状态代码(即内部脚本,而不是外部脚本) 。
For example, suppose I have the a shell script outer-test.sh
: 例如,假设我有一个shell脚本outer-test.sh
:
#!/bin/sh
set -e
./inner-test.sh
exit 62;
The code for inner-test.sh
is: inner-test.sh
的代码是:
#!/bin/sh
exit 26;
When I run outer-script.sh
from the command line my outer script terminates with the exit code of the inner script: 当我从命令行运行outer-script.sh
,我的外部脚本终止于内部脚本的退出代码:
$ ./outer-test.sh
$ echo $?
26
#6楼
This is an old question, but none of the answers here discuss the use of set -e
aka set -o errexit
in Debian package handling scripts. 这是一个老问题,但这里没有一个答案讨论在Debian包处理脚本中使用set -e
aka set -o errexit
。 The use of this option is mandatory in these scripts, per Debian policy; 根据Debian政策,这些脚本中必须使用此选项; the intent is apparently to avoid any possibility of an unhandled error condition. 意图显然是为了避免任何未处理错误情况的可能性。
What this means in practice is that you have to understand under what conditions the commands you run could return an error, and handle each of those errors explicitly. 这在实践中意味着您必须了解在什么条件下运行的命令可能会返回错误,并明确地处理每个错误。
Common gotchas are eg diff
(returns an error when there is a difference) and grep
(returns an error when there is no match). 常见的问题是例如diff
(当存在差异时返回错误)和grep
(当没有匹配时返回错误)。 You can avoid the errors with explicit handling: 您可以通过显式处理来避免错误:
diff this that ||
echo "$0: there was a difference" >&2
grep cat food ||
echo "$0: no cat in the food" >&2
(Notice also how we take care to include the current script's name in the message, and writing diagnostic messages to standard error instead of standard output.) (另请注意我们如何注意在消息中包含当前脚本的名称,以及将诊断消息写入标准错误而不是标准输出。)
If no explicit handling is really necessary or useful, explicitly do nothing: 如果没有真正必要或有用的显式处理,请明确地不做任何事情:
diff this that || true
grep cat food || :
(The use of the shell's :
no-op command is slightly obscure, but fairly commonly seen.) (shell的使用:
no-op命令略显模糊,但很常见。)
Just to reiterate, 重申一下,
something || other
is shorthand for 是简写
if something; then
: nothing
else
other
fi
ie we explicitly say other
should be run if and only if something
fails. 即我们明确地说,当且仅当something
失败时,应该运行other
。 The longhand if
(and other shell flow control statements like while
, until
) is also a valid way to handle an error (indeed, if it weren't, shell scripts with set -e
could never contain flow control statements!) 该手写if
(等外壳流控制之类的语句while
, until
)也是处理错误的一种有效方式(事实上,如果不是,shell脚本set -e
可能不会包含流控制语句!)
And also, just to be explicit, in the absence of a handler like this, set -e
would cause the entire script to immediately fail with an error if diff
found a difference, or if grep
didn't find a match. 而且,只是为了明确,在没有像这样的处理程序的情况下,如果diff
发现差异,或者如果grep
找不到匹配项,则set -e
将导致整个脚本立即失败并显示错误。
On the other hand, some commands don't produce an error exit status when you'd want them to. 另一方面,某些命令在您需要时不会产生错误退出状态。 Commonly problematic commands are find
(exit status does not reflect whether files were actually found) and sed
(exit status won't reveal whether the script received any input or actually performed any commands successfully). 通常有问题的命令是find
(退出状态不反映文件是否实际被发现)和sed
(退出状态不会显示脚本是否接收到任何输入或实际上是否成功执行了任何命令)。 A simple guard in some scenarios is to pipe to a command which does scream if there is no output: 在某些情况下,一个简单的保护是管道输出一个命令,如果没有输出就会尖叫:
find things | grep .
sed -e 's/o/me/' stuff | grep ^
It should be noted that the exit status of a pipeline is the exit status of the last command in that pipeline. 应该注意,管道的退出状态是该管道中最后一个命令的退出状态。 So the above commands actually completely mask the status of find
and sed
, and only tell you whether grep
finally succeeded. 所以上面的命令实际上完全掩盖了find
和sed
的状态,只告诉你grep
是否最终成功了。
(Bash, of course, has set -o pipefail
; but Debian package scripts cannot use Bash features. The policy firmly dictates the use of POSIX sh
for these scripts, though this was not always the case.) (当然,Bash set -o pipefail
;但是Debian包脚本不能使用Bash功能。该策略坚决要求对这些脚本使用POSIX sh
,尽管并非总是这样。)
In many situations, this is something to separately watch out for when coding defensively. 在许多情况下,这是在防御性编码时要单独注意的事情。 Sometimes you have to eg go through a temporary file so you can see whether the command which produced that output finished successfully, even when idiom and convenience would otherwise direct you to use a shell pipeline. 有时您必须通过例如一个临时文件,这样您就可以看到产生该输出的命令是否成功完成,即使成语和方便会导致您使用shell管道。