sou*_*edi 9 shell-script error-handling
errexit ( set -e
) 通常被建议作为一种使简单脚本更加健壮的方法。然而,任何见过它在更复杂的脚本(尤其是函数)中的行为的人通常都会认为这是一个可怕的陷阱。子 shell 也存在非常相似的问题。采取以下示例,改编自/sf/ask/2094820941/ l-on-error
(\nset -o errexit\nfalse\ntrue\n) && echo "OK" || echo "FAILED";\n
Run Code Online (Sandbox Code Playgroud)\n\n这里的陷阱是 shell 显示“OK”,而不是“FAILED”。[*]
\n\n虽然在特定情况下的行为set -e
在历史上以不幸的方式有所不同,但现代发行版上可用的 shell 声称遵循 POSIX shell 标准,并且测试显示以下所有项都有相同的行为(“OK”):
bash-4.4.19-2.fc28.x86_64
(软呢帽)busybox-1.26.2-3.fc27.x86_64
(软呢帽)dash 0.5.8-2.4
(Debian 9)zsh 5.3.1-4+b2
(Debian 9)posh 0.12.6+b1
(Debian 9)ksh 93u+20120801-3.1
(Debian 9)。是否有任何技术原因导致您无法编写具有与 POSIX 类似功能的 shell sh
,除了它打印FAILED
上述代码之外?
我希望它也能改变,set -e
以便返回 false 的顶级&&
表达式被认为是致命的。 https://serverfault.com/a/847016/133475 希望它能有一些更好的习惯用法与 grep 一起使用。
set -e\n\n# print matching lines, but it\'s not an error if there are no matches in the file\n(\nset +e\ngrep pattern file.txt\nRET=$?\n[[ $RET == 0 || $RET == 1 ]] || exit $RET\n)\n
Run Code Online (Sandbox Code Playgroud)\n\n我想我还假设算术语句(let
内置)将以某种方式重新定义,以避免隐式地将其数值强制为真/假退出状态。
有没有一个 shell 的例子可以做到这一点?
\n\n我不介意查看与 Bourne 语法不同的内容。在编写简短的“粘合”脚本时,我对紧凑的东西感兴趣,但它也有一个紧凑的策略来检测错误。我不喜欢用作&&
语句分隔符,如果这意味着意外省略语句分隔符将导致错误被默默忽略。
[*] 编辑。这也许不是一个完美的例子。讨论表明更好的例子是将set -o errexit
子 shell 移到上面。
AFAICT 这与此处(false; echo foo) || echo bar; echo \\ $?
表中的测试用例非常相似,该测试用例表示它将在原始 Bourne shell 及其大多数后代上显示“foo”(然后是“0”)。例外情况是“hist.ash”和 bash 1.14.7。
set -e
当您在子 shell 中添加- (set -e; false; echo foo) || echo bar; echo \\ $?
- 时,有两个额外的例外,“SVR4 sh sun5.10”和 dash-0.3.4。
就我的问题的精神而言,我认为进入set -e
子外壳会分散注意力。set -e
我主要感兴趣的是在脚本顶部使用的习惯用法,它也适用于子 shell(这已经是 POSIX 行为)。
J\xc3\xb6rg Schilling 表明,对于我在这个问题中使用的示例,原始 Unix 的 Bourne shell 会打印“FAILED”,他已将此 shell 作为schilytools 中的“osh”移植到 POSIX ,并且此结果已得到验证截至 2018 年 6 月 11 日发布。也许这是基于 1)set -e
位于子 shell 内部和 2) “SVR4 sh sun5.10”。“osh”“基于 OpenSolaris 源,因此基于 SVR4 和 SVID3”。&& echo "OK"
或者也许在 subshell 和 之间添加会导致一些可怕的额外差异|| echo "FAILED"
。
我认为狡猾的外壳不能回答我的问题。您可以对函数使用子 shell(使用(
代替{
在子 shell 中运行函数),并以 . 启动每个子 shell set -e
。但是,使用子 shell 会带来无法修改全局变量的限制。至少,我还没有看到有人捍卫它作为通用编码风格。
小智 5
关于 shell 选项, POSIX标准(cf -e)比 Bash 手册更清晰errexit
。
启用此选项后,当任何命令失败时(由于 Shell 错误的后果中列出的任何原因或返回大于零的退出状态),shell 应立即退出,就像执行 exit 特殊内置实用程序一样不带任何参数,但以下情况除外:
多命令管道中任何单个命令的失败都不应导致 shell 退出。仅考虑管道本身的故障。
当执行 while、until、if 或 elif 保留字(以 ! 开头的管道)后面的复合列表时,应忽略 -e 设置。保留字,或 AND-OR 列表中除最后一个以外的任何命令。
如果子 shell 命令以外的复合命令的退出状态是在 -e 被忽略时失败的结果,则 -e 不适用于该命令。
此要求分别适用于 shell 环境和每个子 shell 环境。例如,在:
set -e; (false; echo one) | cat; echo two
false 命令导致子 shell 退出而不执行 echo one;然而,执行 echo 2 是因为管道的退出状态 (false; echo one) | 猫为零。
显然,示例代码等效于以下伪代码。
( LIST; ) && COMMAND || COMMAND
Run Code Online (Sandbox Code Playgroud)
列表的退出状态 为零,因为:
shell选项errexit
被忽略。
返回状态是列表中指定的最后一个命令的退出状态(此处)true
。
因此,执行 AND 列表的第二部分:echo "OK"
。