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

我有一个Bash shell脚本,它调用许多命令。

如果任何命令返回非零值,我想让shell脚本自动退出并返回值为1。

如果不显式地检查每个命令的结果,这可能吗?

例如,

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


dosomething2
if [[ $? -ne 0 ]]; then
exit 1
fi
236145 次浏览

将以下内容添加到脚本开头:

set -e

如果一个简单的命令退出时退出值非零,这将导致shell立即退出。简单命令是任何不属于ifwhile,或until测试,或&&||列表的命令。

参见"set"上的bash手册;内部命令获取更多详细信息。

当某个脚本在中间发生故障并打破了对脚本其余部分的假设时,让脚本固执地继续下去是非常令人恼火的。我个人使用set -e启动几乎所有可移植shell脚本。

如果我特别使用bash,我将从

set -Eeuo pipefail

这以类似的方式涵盖了更多的错误处理。我认为这些是新的bash程序的正常默认值。有关这些选项的功能的更多信息,请参阅bash手册。

像这样的表达式

dosomething1 && dosomething2 && dosomething3

当其中一个命令返回非零值时将停止处理。例如,下面的命令永远不会打印"done":

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

-eset -e开头运行它。

再看看set -u

示例中的if语句是不必要的。就像这样做:

dosomething1 || exit 1

如果你采纳了Ville Laurikari的建议并使用set -e,那么对于一些命令你可能需要使用这个:

dosomething || true

|| true将使命令管道具有true返回值,即使命令执行失败,因此-e选项也不会杀死脚本。

如果你需要在退出时进行清理,你也可以使用'trap'和伪信号ERR。这与捕获INT或任何其他信号的方式相同;如果任何命令以非零值退出,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

或者,特别是当你使用"set -e"时,你可以捕获EXIT;然后,当脚本因为任何原因退出时,包括正常结束、中断、由-e选项导致的退出等等,都会执行陷阱。

很少需要$?变量。伪成语command; if [ $? -eq 0 ]; then X; fi应该总是写成if command; then X; fi

需要使用$?的情况是需要对多个值进行检查:

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

或者当$?需要重用或以其他方式操作时:

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

对已接受的答案补充:

请记住,set -e有时是不够的,特别是如果您有管道。

例如,假设您有这个脚本

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

... 正如预期的那样:configure中的错误会中止执行。

明天你要做一个看似微不足道的改变:

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

... 但现在它不管用了。这是解释了在这里,并提供了一个解决方案(仅Bash):

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


./configure  | tee configure.log
make

我只是抛出了另一个参考,因为马克埃德加斯输入了一个额外的问题,这里是一个额外的例子,涉及到整个主题:

[[ `cmd` ]] && echo success_else_silence

这与某人展示的cmd || exit errcode相同。

例如,如果挂载了分区,我想确保分区是卸载的:

[[ `mount | grep /dev/sda1` ]] && umount /dev/sda1
#!/bin/bash -e

应该足够了。

在错误时,下面的脚本将打印一个红色错误消息并退出。
把这个放在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