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)
您需要结束可能失败的每个命令,||并且命令或命令列表评估为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.
关于我的脚本意外死亡的问题,这是由于在子 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)