设置-e和短测试

Ter*_*ior 19 bash shell ksh zsh

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

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

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

set -e
false && true
echo $?
Run Code Online (Sandbox Code Playgroud)

它返回1但是shell不会死!

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

Dan*_*nga 37

单一UNIX规范描述的效果set -e为:

启用此选项时,如果简单命令因Shell错误的后果中列出的任何原因而失败,或者返回退出状态值> 0,并且在while,until或if关键字后面不是复合列表的一部分,并且不是AND或OR列表的一部分,并且不是以!开头的管道!保留字,然后外壳应立即退出.

如您所见,AND列表中的失败命令不会使shell退出.

运用 set -e

启动shell脚本set -e被认为是最佳实践,因为如果发生某些错误,通常会更安全地中止脚本.如果命令可能无害地失败,我通常会附加|| true它.

这是一个简单的例子:

#!/bin/sh
set -e

# [...]
# remove old backup files; do not fail if none exist
rm *~ *.bak || true
Run Code Online (Sandbox Code Playgroud)

  • 我说错了,子shell没有,但是由于子shell返回"1"而导致shell死掉.在这种情况下,使用`if`或`||:`更安全 (5认同)
  • 为了这个例子,让我们忽略`rm -f`的存在. (4认同)

Paw*_*ski 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                      
}
Run Code Online (Sandbox Code Playgroud)

false && true不会导致退出,因为结束表达式被计算并且计算结果为0.从bash手册页中set -e:

如果管道(可能包含单个简单命令),括在括号中的子shell命令,或作为括号括起来的命令列表的一部分执行的命令之一(参见上面的SHELL GRAMMAR)退出时,则立即退出状态.如果失败的命令是紧跟在while或until关键字之后的命令列表的一部分,在if或elif保留字之后的测试的一部分,在&&或||中执行的任何命令的一部分,则shell不会退出 列表除了最后的&&或||之后的命令 ,管道中的任何命令,但是最后一个,或者命令的返回值是否被反转!ERR上的陷阱(如果已设置)将在shell退出之前执行.此选项分别适用于shell环境和每个子shell环境(请参阅上面的COMMAND EXECUTION ENVIRONMENT),并且可能会导致子shell在执行子shell中的所有命令之前退出.

只有在||/ &&chain中执行的最后一个命令才能触发退出.

由于上述原因,以下表达式将失败.

true && false
Run Code Online (Sandbox Code Playgroud)

但以下不会:

if true && false                                                                
then                                                                            
  true                                                                          
fi                   
Run Code Online (Sandbox Code Playgroud)

因为true && false是测试的一部分if.


Ter*_*ior 2

关于我的脚本意外死亡的问题,这是由于在子 shell 中或从函数中运行这些简短的测试,并且所述函数返回不为零的值:

原始示例:

$ set -e
$ false && true
$ echo $?
1
Run Code Online (Sandbox Code Playgroud)

使用子 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>
Run Code Online (Sandbox Code Playgroud)

可能的解决方案:

$ set -e
$ (false && true ||:)
$ (false && true) ||:
$ (if false; then true; fi)
Run Code Online (Sandbox Code Playgroud)