我有一个bash shell脚本,它调用许多命令。如果任何命令返回非零值,我希望shell脚本自动退出,返回值为1。
如果不显式检查每个命令的结果,是否可以这样做?
例如
dosomething1
if [[ $? -ne 0 ]]; then
exit 1
fi
dosomething2
if [[ $? -ne 0 ]]; then
exit 1
fi
除set -e外,还包括set -u或set -eu。-u结束了愚蠢的、隐藏错误的行为,您可以访问任何不存在的变量,并在不进行诊断的情况下生成一个空白值。
将此添加到脚本开头:
set -e
如果一个简单的命令以非零的退出值退出,这将导致shell立即退出。简单命令是不属于if、while或until测试的任何命令,或者是&;&;或列表的一部分。
有关更多详细信息,请参阅"set"内部命令上的bash(1)手册页。
我个人用"set-e"开始几乎所有shell脚本。当一个脚本在中间发生故障,并且破坏了脚本其余部分的假设时,让它顽固地继续下去,真的很烦人。
那是可行的,但我喜欢用""!/usr/bin/env bash"因为我经常从/bin之外的其他地方运行bash。还有"啊!/usr/bin/env bash-e"不起作用。此外,当我想打开跟踪以进行调试时,最好有一个地方可以修改为"set-xe"。
记住,由于管道的存在,这可能还不够。查看我添加的答案。
此外,如果脚本以bash script.sh的形式运行,则会忽略shebang行上的标志。
只需注意:如果您在bash脚本中声明函数,那么如果您想要扩展这个功能,这些函数需要在函数体中重新声明set-e。
另外,如果你源代码你的脚本,shebang行将是不相关的。
@在bash 3.2.48中似乎没有这种情况。在脚本中尝试以下操作:set -e; tf() { false; }; tf; echo 'still here'。即使没有set -e在tf()体内,执行也会中止。也许你是想说,set -e不是由子类继承的,这是事实。
@Villeraurikari这是为现有的命令,但如何知道手动命令的状态(假设如果我执行了一个程序,如何知道它的状态,不管是成功还是错误)。我有一个场景需要实现,在这里我必须执行n个命令(每个命令都作为线程运行),当其中任何一个失败时,我必须退出脚本。
您也可以稍后在脚本中再次禁用set +e的错误失败。
这是一个很好的答案——但要仔细测试。-e(errexit)在不同版本的bash中是不一致的,除非您非常了解您的脚本,否则您可能会被管道或其他命令中的错误所捕获。您将捕获想要捕获的错误,但在复杂的脚本中,您不会捕获每个错误。
所以脚本中有一些命令即使失败也不会终止脚本。有没有一种方法可以为脚本中有set-e的特定命令设置异常?
要添加到接受的答案中:
记住,有时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
感谢您解释让pipefail与set -o一起使用的重要性!
示例中的if语句是不必要的。就这样做:
dosomething1 || exit 1
如果您采纳Ville Laurikari的建议并使用set -e,那么对于某些命令,您可能需要使用以下命令:
dosomething || true
即使命令失败,|| true也会使命令管道具有true返回值,因此-e选项不会终止脚本。
我喜欢这个。尤其是因为最重要的答案是以bash为中心的(我根本不清楚它是否/在多大程度上适用于zsh脚本)。我可以查一下,但你的逻辑更清楚。
如果您需要在退出时进行清理,也可以将"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",则可以陷阱退出;然后,当脚本出于任何原因退出时,将执行陷阱,包括正常结束、中断、由-e选项引起的退出等。
运行时,-e或set -e位于顶部。
也可以看看set -u。
为了潜在地拯救其他人,需要通读help set:-u将对未设置变量的引用视为错误。
所以要么是set -u要么是set -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
为什么"应该总是写为"?我是说,为什么"应该"是这样?当一个命令很长时(考虑使用十几个选项调用gcc),那么在检查返回状态之前运行该命令更容易阅读。
如果命令太长,可以通过命名(定义shell函数)将其分解。
谢谢,正是我要找的…
#!/bin/bash -e
应该足够了。
像这样的表达
dosomething1 && dosomething2 && dosomething3
当其中一个命令返回非零值时将停止处理。例如,以下命令将永远不会打印"完成":
cat nosuchfile && echo"done"
echo $?
1
因为有一个额外的问题要标记Edgars的输入,所以只需插入另一个问题作为参考,这里还有一个额外的示例,并涉及到整个主题:
[[ `cmd` ]] && echo success_else_silence
这和有人展示的cmd || exit errcode是一样的。
我想确保如果安装了分区,就可以卸载它:
[[ `mount | grep /dev/sda1` ]] && umount /dev/sda1
不,[[cmd`]`不是一回事。如果命令的输出为空,则为假;否则为真,无论命令的退出状态如何。