简体   繁体   English

如果任何命令返回非零值,则中止 shell 脚本

[英]Aborting a shell script if any command returns a non-zero value

I have a Bash shell script that invokes a number of commands.我有一个调用许多命令的 Bash shell 脚本。

I would like to have the shell script automatically exit with a return value of 1 if any of the commands return a non-zero value.如果任何命令返回非零值,我想让 shell 脚本自动退出,返回值为 1。

Is this possible without explicitly checking the result of each command?如果不明确检查每个命令的结果,这可能吗?

For example,例如,

dosomething1
if [[ $? -ne 0 ]]; then
    exit 1
fi

dosomething2
if [[ $? -ne 0 ]]; then
    exit 1
fi

Add this to the beginning of the script:将此添加到脚本的开头:

set -e

This will cause the shell to exit immediately if a simple command exits with a nonzero exit value.如果一个简单的命令以非零退出值退出,这将导致 shell 立即退出。 A simple command is any command not part of an if, while, or until test, or part of an && or ||一个简单的命令是不属于 if、while 或 until 测试的一部分,或者不属于 && 或 || 的任何命令list.列表。

See the bash(1) man page on the "set" internal command for more details.有关更多详细信息,请参阅“set”内部命令上的bash(1) 手册页

I personally start almost all shell scripts with "set -e".我个人以“set -e”开始几乎所有的 shell 脚本。 It's really annoying to have a script stubbornly continue when something fails in the middle and breaks assumptions for the rest of the script.当中间出现故障并破坏脚本其余部分的假设时,让脚本顽固地继续下去真的很烦人。

To add to the accepted answer:添加到接受的答案:

Bear in mind that set -e sometimes is not enough, specially if you have pipes.请记住set -e有时是不够的,特别是如果您有管道。

For example, suppose you have this script例如,假设你有这个脚本

#!/bin/bash
set -e 
./configure  > configure.log
make

... which works as expected: an error in configure aborts the execution. ...按预期工作: configure的错误中止执行。

Tomorrow you make a seemingly trivial change:明天你做一个看似微不足道的改变:

#!/bin/bash
set -e 
./configure  | tee configure.log
make

... and now it does not work. ......现在它不起作用。 This is explained here , and a workaround (Bash only) is provided:此处对此进行了解释,并提供了一种解决方法(仅限 Bash):

#!/bin/bash
set -e 
set -o pipefail

./configure  | tee configure.log
make

The if statements in your example are unnecessary.您示例中的 if 语句是不必要的。 Just do it like this:只需这样做:

dosomething1 || exit 1

If you take Ville Laurikari's advice and use set -e then for some commands you may need to use this:如果您接受 Ville Laurikari 的建议并使用set -e那么对于某些命令,您可能需要使用以下命令:

dosomething || true

The || true || true || true will make the command pipeline have a true return value even if the command fails so the the -e option will not kill the script.即使命令失败, || true也会使命令管道具有true返回值,因此-e选项不会终止脚本。

If you have cleanup you need to do on exit, you can also use 'trap' with the pseudo-signal ERR.如果您需要在退出时进行清理,您还可以使用带有伪信号 ERR 的“陷阱”。 This works the same way as trapping INT or any other signal;这与捕获 INT 或任何其他信号的工作方式相同; bash throws ERR if any command exits with a nonzero value:如果任何命令以非零值退出,bash 将抛出 ERR:

# Create the trap with   
#    trap COMMAND SIGNAME [SIGNAME2 SIGNAME3...]
trap "rm -f /tmp/$MYTMPFILE; exit 1" ERR INT TERM
command1
command2
command3
# Partially turn off the trap.
trap - ERR
# Now a control-C will still cause cleanup, but
# a nonzero exit code won't:
ps aux | grep blahblahblah

Or, especially if you're using "set -e", you could trap EXIT;或者,特别是如果您使用“set -e”,您可能会陷入 EXIT; your trap will then be executed when the script exits for any reason, including a normal end, interrupts, an exit caused by the -e option, etc.当脚本出于任何原因退出时,您的陷阱将被执行,包括正常结束、中断、由 -e 选项引起的退出等。

The $? $? variable is rarely needed.很少需要变量。 The pseudo-idiom command; if [ $? -eq 0 ]; then X; fi伪习语command; if [ $? -eq 0 ]; then X; fi command; if [ $? -eq 0 ]; then X; fi command; if [ $? -eq 0 ]; then X; fi should always be written as if command; then X; fi command; if [ $? -eq 0 ]; then X; fi应该总是写成if command; then X; fi if command; then X; fi if command; then X; fi . if command; then X; fi

The cases where $? $?的情况下$? is required is when it needs to be checked against multiple values:当需要针对多个值进行检查时是必需的:

command
case $? in
  (0) X;;
  (1) Y;;
  (2) Z;;
esac

or when $?或者什么时候$? needs to be reused or otherwise manipulated:需要重用或以其他方式操作:

if command; then
  echo "command successful" >&2
else
  ret=$?
  echo "command failed with exit code $ret" >&2
  exit $ret
fi

Run it with -e or set -e at the top.在顶部使用-eset -e运行它。

Also look at set -u .也看看set -u

On error, the below script will print a RED error message and exit.出错时,以下脚本将打印 RED 错误消息并退出。
Put this at the top of your bash script:将其放在 bash 脚本的顶部:

# BASH error handling:
#   exit on command failure
set -e
#   keep track of the last executed command
trap 'LAST_COMMAND=$CURRENT_COMMAND; CURRENT_COMMAND=$BASH_COMMAND' DEBUG
#   on error: print the failed command
trap 'ERROR_CODE=$?; FAILED_COMMAND=$LAST_COMMAND; tput setaf 1; echo "ERROR: command \"$FAILED_COMMAND\" failed with exit code $ERROR_CODE"; put sgr0;' ERR INT TERM
#!/bin/bash -e

应该足够了。

An expression like像这样的表达

dosomething1 && dosomething2 && dosomething3

will stop processing when one of the commands returns with a non-zero value.当其中一个命令返回非零值时,将停止处理。 For example, the following command will never print "done":例如,以下命令永远不会打印“done”:

cat nosuchfile && echo "done"
echo $?
1

I am just throwing in another one for reference since there was an additional question to Mark Edgars input and here is an additional example and touches on the topic overall:我只是抛出另一个供参考,因为 Mark Edgars 的输入还有一个额外的问题,这里是一个额外的例子,涉及整个主题:

[[ `cmd` ]] && echo success_else_silence

Which is the same as cmd || exit errcodecmd || exit errcode相同cmd || exit errcode as someone showed.如某人所示cmd || exit errcode

For example, I want to make sure a partition is unmounted if mounted:例如,如果已安装,我想确保卸载分区:

[[ `mount | grep /dev/sda1` ]] && umount /dev/sda1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM