设置-e和短测试

时间:2011-08-03 17:05:08

标签: bash shell ksh zsh

当我刚接触shell脚本时,我使用了很多简短测试而不是if语句,如false && true

后来我学会了使用set -e,发现我的脚本由于某种原因而死亡,如果用完整的if语句替换短测试,它们就可以工作。现在,时间已经过去了,我仍然只使用完整的if语句。

最有趣的是,如果我打开一个交互式shell并执行以下操作:

set -e
false && true
echo $?

它返回1,但shell不会死!

我看到我浪费了太多代码。任何人都可以向我解释如何安全地使用set -e进行短测试,例如。没有剧本死亡?

4 个答案:

答案 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 && falseif之后的测试的一部分。

答案 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)