Linux set -e 在 bash 脚本中是什么意思?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19622198/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-07 01:12:05  来源:igfitidea点击:

What does set -e mean in a bash script?

linuxbashshellsh

提问by AndreaNobili

I'm studying the content of this preinstfile 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 [ "" = 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. 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 1mean that ends with an “error”, doesn't it?).

我认为脚本的其余部分非常简单:它检查 Debian/Ubuntu 包管理器是否正在执行安装操作。如果是,它会检查我的应用程序是否刚刚安装在系统上。如果有,脚本将打印消息“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?

这是正确的还是我错过了什么?

采纳答案by Gilles Quenot

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). It's recommended to use:

但有些人认为这是不好的做法(bash FAQ 和 irc freenode #bash FAQ 作者)。建议使用:

trap 'do_something' ERR

to run do_somethingfunction when errors occur.

do_something发生错误时运行函数。

See http://mywiki.wooledge.org/BashFAQ/105

http://mywiki.wooledge.org/BashFAQ/105

回答by Robin Green

set -estops 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. Type help setin a terminal to see the documentation for this built-in command.

set -e如果命令或管道有错误,则停止脚本的执行 - 这与默认的 shell 行为相反,即忽略脚本中的错误。键入help set在终端看到的文档,这个内置的命令。

回答by kenorb

As per bash - The Set Builtinmanual, if -e/errexitis set, the shell exits immediately if a pipelineconsisting of a single simple command, a listor a compound commandreturns a non-zero status.

根据bash - The Set Builtin手册,如果设置了-e/ errexit,如果由单个简单命令列表复合命令组成的管道返回非零状态,则外壳会立即退出。

By default, the exit status of a pipeline is the exit status of the last command in the pipeline, unless the pipefailoption 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 onexitis 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/errtracewhich would trap on ERR instead, e.g.:

有类似的选项-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 pipefailbeing 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 pipefailbeing enabled:

pipefail启用测试:

$ bash -c 'set -o pipefail -e; true | false | true; echo success'; echo $?
1

回答by entpnerd

I found this post while trying to figure out what the exit status was for a script that was aborted due to a set -e. The answer didn't appear obvious to me; hence this answer. Basically, set -eaborts the execution of a command (e.g. a shell script) and returns the exit status code of the command that failed (i.e. the inner script, not the outer script).

我在试图找出由于set -e. 答案对我来说并不明显。因此这个答案。基本上,set -e中止命令的执行(例如,shell 脚本)并返回失败的命令的退出状态代码(即内部脚本,而不是外部脚本)

For example, suppose I have the shell script outer-test.sh:

例如,假设我有 shell 脚本outer-test.sh

#!/bin/sh
set -e
./inner-test.sh
exit 62;

The code for inner-test.shis:

代码为inner-test.sh

#!/bin/sh
exit 26;

When I run outer-script.shfrom the command line, my outer script terminates with the exit code of the inner script:

当我从命令行运行时outer-script.sh,我的外部脚本以内部脚本的退出代码终止:

$ ./outer-test.sh
$ echo $?
26

回答by tripleee

This is an old question, but none of the answers here discuss the use of set -eaka set -o errexitin Debian package handling scripts. The use of this option is mandatoryin these scripts, per Debian policy; the intent is apparently to avoid any possibility of an unhandled error condition.

这是一个老问题,但这里的答案都没有讨论在 Debian 包处理脚本中使用set -eaka set -o errexit。根据Debian 政策,在这些脚本中必须使用此选项;目的显然是为了避免任何未处理错误情况的可能性。

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 e.g. diff(returns an error when there is a difference) and grep(returns an error when there is no match). You can avoid the errors with explicit handling:

常见的问题是例如diff(当有差异时返回错误)和grep(当没有匹配时返回错误)。您可以通过显式处理避免错误:

diff this that ||
  echo "
diff this that || true
grep cat food || :
: there was a difference" >&2 grep cat food || echo "
something || other
: 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:

如果确实没有必要或有用的显式处理,则显式不执行任何操作:

if something; then
    : nothing
else
    other
fi

(The use of the shell's :no-op command is slightly obscure, but fairly commonly seen.)

(shell 的:no-op 命令的使用有点晦涩,但很常见。)

Just to reiterate,

只是重申一下,

find things | grep .
sed -e 's/o/me/' stuff | grep ^

is shorthand for

是简写

#!/bin/bash 
# set -e

lsd 

ls

i.e. we explicitly say othershould be run if and only if somethingfails. 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 -ecould never contain flow control statements!)

即我们明确说other当且仅当something失败时应该运行。简写if(以及其他 shell 流控制语句,如while, until)也是处理错误的有效方法(事实上,如果不是这样,shell 脚本set -e永远不会包含流控制语句!)

And also, just to be explicit, in the absence of a handler like this, set -ewould cause the entire script to immediately fail with an error if difffound a difference, or if grepdidn't find a match.

而且,明确地说,在没有这样的处理程序的情况下,set -e如果diff发现差异或grep找不到匹配项,将导致整个脚本立即失败并显示错误。

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). A simple guard in some scenarios is to pipe to a command which does scream if there is no output:

另一方面,某些命令在您希望它们时不会产生错误退出状态。常见的有问题的命令是find(退出状态不反映是否实际找到文件)和sed(退出状态不会显示脚本是否收到任何输入或实际成功执行了任何命令)。在某些情况下,一个简单的保护是通过管道传递到一个命令,如果没有输出,它会尖叫:

Script 1: without setting -e
#!/bin/bash
decho "hi"
echo "hello"
This will throw error in decho and program continuous to next line

Script 2: With setting -e
#!/bin/bash
set -e
decho "hi" 
echo "hello"
# Up to decho "hi" shell will process and program exit, it will not proceed further

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 findand sed, and only tell you whether grepfinally succeeded.

需要注意的是,管道的退出状态是该管道中最后一个命令的退出状态。所以上面的命令实际上完全掩盖了findand的状态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 shfor these scripts, though this was not always the case.)

(当然,Bash 有set -o pipefail; 但 Debian 包脚本不能使用 Bash 功能。该政策明确规定sh这些脚本使用 POSIX ,尽管情况并非总是如此。)

In many situations, this is something to separately watch out for when coding defensively. Sometimes you have to e.g. 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 管道。

回答by Kallin Nagelberg

I believe the intention is for the script in question to fail fast.

我相信目的是让有问题的脚本快速失败。

To test this yourself, simply type set -eat a bash prompt. Now, try running ls. You'll get a directory listing. Now, type lsd. That command is not recognized and will return an error code, and so your bash prompt will close (due to set -e).

要自己测试,只需set -e在 bash 提示符下键入。现在,尝试运行ls. 你会得到一个目录列表。现在,键入lsd。该命令无法识别并将返回错误代码,因此您的 bash 提示符将关闭(由于set -e)。

Now, to understand this in the context of a 'script', use this simple script:

现在,要在“脚本”的上下文中理解这一点,请使用这个简单的脚本:

##代码##

If you run it as is, you'll get the directory listing from the lson the last line. If you uncomment the set -eand run again, you won't see the directory listing as bash stops processing once it encounters the error from lsd.

如果按原样运行它,您将获得ls最后一行的目录列表。如果您取消注释set -e并再次运行,您将不会看到目录列表,因为 bash 一旦遇到来自 .bashrc 的错误就会停止处理lsd

回答by Manikandan Raj

##代码##