set -e在bash脚本中的含义是什么?

本文探讨了在bash脚本中使用set -e的含义,它会使脚本在遇到错误时立即退出。文章通过例子解释了set -e如何影响脚本的执行,以及在Debian包处理脚本中使用set -e的必要性,强调了处理错误和避免脚本意外继续的重要性。
摘要由CSDN通过智能技术生成

本文翻译自: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 (等外壳流控制之类的语句whileuntil )也是处理错误的一种有效方式(事实上,如果不是,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. 所以上面的命令实际上完全掩盖了findsed的状态,只告诉你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管道。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值