return用于函数局部返回状态,exit用于脚本全局终止;函数内必须显式return并禁用exit,主流程统一用exit收口,通过$?捕获状态,source场景下避免裸exit。

在 Shell 脚本中,return 和 exit 不是可互换的退出手段,而是承担不同职责的控制原语:前者用于函数内部的状态反馈,后者用于整个脚本进程的终止决策。要精确区分“函数局部错误”与“工作流全局状态”,关键在于**分层返回、状态隔离、调用链显式捕获**——不是靠猜,而是靠结构设计。
函数内只用 return,且始终显式声明返回值
return 是函数作用域内的“局部出口”,它不会中断脚本执行,只把控制权交还给调用者,并附带一个 0–255 的整数状态码。这个状态码应代表该函数自身的逻辑成败,与整个工作流无关。
- 成功返回
return 0,失败返回非零(如return 1表示通用错误,return 2表示参数无效) - 禁止省略 return 值:隐式返回会取最后一条命令的
$?,容易污染语义 - 函数内不出现
exit,除非你明确想在此处“炸掉整个脚本”——这已不属于局部错误处理范畴
主流程用 exit 统一收口,且仅在顶层判定后触发
exit 是脚本级的“终局开关”。它应在所有函数调用完成、所有局部状态被评估之后,由主逻辑统一决定是否退出、以何种状态退出。
- 不要在函数里写
exit 1来“报错”,这会让调用方失去干预权;应return 1,让上层 decide - 主流程中用
if ! my_check_func; then echo "检查失败"; exit 1; fi这类模式,把“是否终止工作流”的决策权保留在顶层 - exit 的状态码应反映工作流整体结果:0 表示全流程成功;1 表示不可恢复错误;2 表示部分跳过但主体完成(按需自定义)
用 $? 精确捕获上一级 return 或命令状态,避免状态丢失
Shell 中 $? 总是保存上一条命令(含函数调用)的退出状态。它是连接局部与全局的唯一可信信道。
- 每次调用函数后立即检查
$?,不要隔多条命令再查,否则会被中间命令覆盖 - 可封装为工具函数:
assert_ok() { if [ "$?" -ne 0 ]; then echo "断言失败: $1"; exit 1; fi; } - 避免在管道或子 shell 中直接依赖
$?(如cmd | grep xxx; echo $?返回的是 grep 状态),需用set -o pipefail或显式捕获
source 执行时特别注意 exit 的副作用
当脚本通过 source 或 . 被加载进当前 shell(常见于运维环境的模块化加载),exit 会直接退出当前终端会话——这不是 bug,是机制使然。
- 若脚本可能被 source,就绝不能在其中使用裸
exit;改用return+ 外层兜底判断 - 推荐做法:模块脚本只提供函数和
return,由独立的 runner 脚本负责exit决策 - 可通过
(exit) || echo "正在被 source"检测执行上下文,但更稳妥的是约定规范而非运行时探测


















