这是一个非常经典且重要的 Shell 执行机制问题。直接给结论:
- 会新建子进程(子 Shell)吗?
会。 使用bash $PushAssets会启动一个新的 Bash 进程来运行该脚本。 set -e能触发退出吗?
能。 如果子脚本返回非 0 值,父脚本在set -e模式下会立即终止。
下面是详细的拆解和注意事项:
1. 进程模型
当你执行 bash $PushAssets 时:
- 当前 Shell(父进程)会调用
fork和exec启动一个全新的bash进程。 - 父进程会挂起(Wait),等待子进程结束。
- 子进程执行完毕后,会将它的退出状态码(Exit Code)传回给父进程。
2. set -e 的工作原理
在父脚本中设置 set -e 后,Shell 会检查每一个命令的返回值。
- 如果命令是
bash $PushAssets,父脚本拿到的返回值就是该子脚本中最后一条执行命令的返回值(或者是子脚本中显式调用的exit X的值)。 - 只要这个返回值 != 0,父脚本就会认为这一行命令失败了,从而触发退出。
3. 这里的“陷阱”:子脚本内部是否设置了 set -e?
这是最容易出错的地方。请看以下两个场景:
场景 A:子脚本内部没有 set -e
1 | # sub.sh (子脚本) |
如果你在主脚本执行 bash sub.sh,子脚本最后一行 echo 成功了,所以整个子脚本的返回值是 0。此时父脚本的 set -e 不会触发退出,因为它认为子脚本成功执行了。
场景 B:子脚本内部也写了 set -e(或者显式报错)
1 | # sub.sh (子脚本) |
此时执行 bash sub.sh,返回值是 2。父脚本的 set -e 会立即感知并触发退出。
4. 最佳实践建议
为了确保链路上的错误能百分之百传递给最外层,建议:
- **所有的脚本头部都加上
set -e**。 - 如果是为了环境变量或共享上下文,考虑使用
source而不是bash:-
source $PushAssets(或. $PushAssets):在当前进程中运行。 - 如果子脚本出错(且子脚本有
set -e),它会直接结束当前进程。
-
- 如果使用了管道(Pipe):
如果在调用处写了bash $PushAssets | tee log.txt,默认情况下即便子脚本崩溃,只要tee成功了,父脚本就不会退出。此时需要加上:1
2set -e
set -o pipefail # 确保管道中任何一环出错都算整体出错
总结
你的写法 bash $PushAssets 配合 set -e 是有效的,但前提是 $PushAssets 那个脚本本身要能返回非零状态码(最简单的办法就是在子脚本里也加上 set -e)。