在bash手册页中,它指出:
如果管道(可能包含一个简单的命令),
括在括号中的子shell命令,或者作为括号括起来的命令列表的一部分执行的命令之一,则立即退出...
所以我假设一个函数应该被视为括号括起来的命令列表.但是,如果对函数调用应用条件,则errexit不再在函数体内持久存在,并且在返回之前执行整个命令列表.即使您在为该子shell启用了errexit的函数内显式创建子shell,也会执行命令列表中的所有命令.这是一个演示该问题的简单示例:
function a() { b ; c ; d ; e ; }
function ap() { { b ; c ; d ; e ; } ; }
function as() { ( set -e ; b ; c ; d ; e ) ; }
function b() { false ; }
function c() { false ; }
function d() { false ; }
function e() { false ; }
Run Code Online (Sandbox Code Playgroud)
( set -Eex ; a )
+ a
+ b
+ false
Run Code Online (Sandbox Code Playgroud)
( set -Eex ; ap )
+ ap
+ b
+ false
Run Code Online (Sandbox Code Playgroud)
( set -Eex ; as )
+ as
+ set -e
+ b
+ false
Run Code Online (Sandbox Code Playgroud)
现在如果我对每个人都应用条件......
( set -Eex ; a || false )
+ a
+ b
+ false
+ c
+ false
+ d
+ false
+ e
+ false
+ false
Run Code Online (Sandbox Code Playgroud)
( set -Eex ; ap || false )
+ ap
+ b
+ false
+ c
+ false
+ d
+ false
+ e
+ false
+ false
Run Code Online (Sandbox Code Playgroud)
( set -Eex ; as )
+ as
+ set -e
+ b
+ false
+ c
+ false
+ d
+ false
+ e
+ false
+ false
Run Code Online (Sandbox Code Playgroud)
Gar*_*ees 12
你开始引用手册,然后你切掉了解释这种行为的位,这是在下一句话中:
-e如果管道(可能包含一个简单命令,括在括号中的子shell命令)或作为由括号括起的命令列表的一部分执行的命令之一返回非零状态,则立即退出.如果失败的命令是紧跟在一个或关键字之后的命令列表的一部分,一个语句中的测试的一部分,在一个或列表中执行的任何命令的一部分,除了在final之后的命令或者任何命令之外,shell都不会退出管道但最后一个,或者如果命令的返回状态被反转.whileuntilif&&||&&||!
bug-bash邮件列表有一个Eric Blake对函数更明确的解释:
简答:历史兼容性.
...
实际上,POSIX强制要求的正确行为(即'set -e'在整个f()体的持续时间内被完全忽略,因为f在忽略'set -e'的上下文中被调用)不直观.但它是标准化的,所以我们必须忍受它.
还有一些关于是否set -e可以被利用来实现想要的行为的话:
因为一旦你处于忽略'set -e'的上下文中,历史行为就是没有进一步的方法来重新打开它,因为在被忽略的上下文中的整个代码体.这就是30年前,在真正考虑shell功能之前,它是如何完成的,我们仍然坚持那个糟糕的设计决策.