我有一个调用许多命令的 Bash shell 脚本。
如果任何命令返回非零值,我希望 shell 脚本自动退出并返回值 1。
这是否可以在不显式检查每个命令的结果的情况下实现?
例如,
dosomething1
if [[ $? -ne 0 ]]; then
exit 1
fi
dosomething2
if [[ $? -ne 0 ]]; then
exit 1
fi
将其添加到脚本的开头:
set -e
如果简单命令以非零退出值退出,这将导致 shell 立即退出。 简单命令是指不属于
if
、while
或 until
测试的一部分,或者不属于 &&
或 ||
列表的任何命令。
有关“set”内部命令的更多详细信息,请参阅 bash 手册。
当中间发生某些事情并打破了脚本其余部分的假设时,让脚本顽固地继续下去真的很烦人。 我个人几乎所有的可移植 shell 脚本都是用
set -e
开始的。
如果我专门使用 bash,我将从
开始set -Eeuo pipefail
这涵盖了以类似方式进行的更多错误处理。 我认为这些是新 bash 程序的合理默认值。 有关这些选项的作用的更多信息,请参阅 bash 手册。
添加到已接受的答案:
请记住,
set -e
有时是不够的,特别是如果你有管道的话。
例如,假设您有这个脚本
#!/bin/bash
set -e
./configure > configure.log
make
...按预期工作:
configure
中的错误会中止执行。
明天你要做一个看似微不足道的改变:
#!/bin/bash
set -e
./configure | tee configure.log
make
...现在不起作用了。 here对此进行了解释,并提供了解决方法(仅限 Bash):
#!/bin/bash 设置-e 设置-o管道故障 ./配置| tee配置.log 制作
示例中的 if 语句是不必要的。 就这样做吧:
dosomething1 || exit 1
如果您采纳 Ville Laurikari 的建议并使用
set -e
那么对于某些命令,您可能需要使用此:
dosomething || true
即使命令失败,
|| true
将使命令管道具有true
返回值,因此-e
选项不会终止脚本。
如果您需要在退出时进行清理,您还可以使用带有伪信号 ERR 的“trap”。这与捕获 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
使用顶部的
-e
或 set -e
运行它。
另请参阅
set -u
。
出现错误时,以下脚本将打印一条带有失败命令的红色错误消息,然后退出。
将其放在 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"; tput sgr0;' ERR INT TERM
#!/bin/bash -e
应该足够了。
类似的表达方式
dosomething1 && dosomething2 && dosomething3
当其中一个命令返回非零值时,将停止处理。例如,以下命令永远不会打印“done”:
cat nosuchfile && echo "done"
echo $?
1
我只是提出另一个问题以供参考,因为 Mark Edgars 还提出了一个额外的问题,这里有一个额外的示例,涉及整个主题:
[[ `cmd` ]] && echo success_else_silence
这与某人展示的
cmd || exit errcode
相同。
例如,我想确保分区在已安装时被卸载:
[[ `mount | grep /dev/sda1` ]] && umount /dev/sda1