当我刚接触shell脚本时,我使用了很多简短测试而不是if
语句,如false && true
。
后来我学会了使用set -e
,发现我的脚本由于某种原因而死亡,如果用完整的if
语句替换短测试,它们就可以工作。现在,时间已经过去了,我仍然只使用完整的if
语句。
最有趣的是,如果我打开一个交互式shell并执行以下操作:
set -e
false && true
echo $?
它返回1,但shell不会死!
我看到我浪费了太多代码。任何人都可以向我解释如何安全地使用set -e
进行短测试,例如。没有剧本死亡?
答案 0 :(得分:36)
Single UNIX Specification描述了set -e
的效果:
当此选项打开时,如果简单命令因Shell错误的后果中列出的任何原因而失败,或者返回退出状态值> 0,并且在一段时间之后不是复合列表的一部分,直到或if关键字,并且不是AND或OR列表的一部分,并且不是以!开头的管道!保留字,然后外壳应立即退出。
如您所见,AND列表中的失败命令不会使shell退出。
使用set -e
使用set -e
启动shell脚本被认为是最佳做法,因为如果发生某些错误,通常会更安全地中止脚本。如果命令可能无害地失败,我通常会向其添加|| true
。
这是一个简单的例子:
#!/bin/sh
set -e
# [...]
# remove old backup files; do not fail if none exist
rm *~ *.bak || true
答案 1 :(得分:5)
您需要结束可能因||
而失败的每个命令以及评估为0的命令或命令列表。如果运算符之前的表达式未计算为0,则使用||
将触发您的命令。命令需要求值为0才能杀死shell。
示例:
set -e
false || true # silently ignore error
false || echo "Command failed, but exit status of echo is 0. Continuing..."
false || {
echo "Command failed. Continuing..."
# do something else
false && true # all commands in the list need to be true
}
false && true
不会导致退出,因为结束表达式已计算并且计算结果为0.从set -e
的bash手册页:
如果管道(可能由一个管道组成)立即退出 简单命令),括在括号中的子shell命令,或者其中之一 作为由大括号括起的命令列表的一部分执行的命令 (参见上面的SHELL GRAMMAR)以非零状态退出。 外壳 如果失败的命令是命令列表的一部分,则不会退出 马上关注一段时间或直到关键词,测试的一部分 在if 或elif保留字之后,执行任何命令的一部分 在&&或||列表除了最后一个&&之后的命令或|| , 管道中的任何命令,但是最后一个,或者命令的返回 价值正在被反转! ERR上的陷阱(如果已设置)将被执行 在shell退出之前。此选项适用于shell环境 和每个子shell环境分开(参见COMMAND EXECUTION 上面的环境),并可能导致子壳在执行之前退出 子shell中的所有命令。
只有在||
/ &&
链中执行的最后一个命令才能触发退出。
以上表达式将因上述原因而失败。
true && false
但以下不会:
if true && false
then
true
fi
因为true && false
是if
之后的测试的一部分。
答案 2 :(得分:1)
除了最简单的情况外,set -e
的行为在所有情况下都是不直观的,并且多年来已经多次改变。因此,除了非常简单的脚本(普通命令序列)之外,我不建议使用它。
请注意,Makefile中的命令通常在set -e
生效的情况下执行,因此仍然需要了解它的工作原理。
&&
和||
运算符允许类似的效果而且杂乱程度相对较小,但要明确。特别是,&&
可能放在一行的末尾,就像`;'。
答案 3 :(得分:1)
关于我的脚本意外死亡的问题,这是由于在子shell或函数中运行这些短测试,并且所述函数返回的值不同于零:
原始示例:
$ set -e
$ false && true
$ echo $?
1
使用子shell或函数:
$ set -e
$ (false && true)
<script died>
$ set -e
$ variable=$(false && true)
<script died>
$ set -e
$ foo()(false && true)
$ foo
<script died>
$ set -e
$ foo(){ false && true; }
$ foo
<script died>
可能的解决方案:
$ set -e
$ (false && true ||:)
$ (false && true) ||:
$ (if false; then true; fi)