Shell 错误处理、调试与资源清理
# 错误处理与健壮性
# 严格模式
set -e # 在适用上下文中遇非零状态退出;存在下述例外
set -u # 引用未定义变量报错
set -o pipefail # 管道状态取最右侧非零状态;全成功才是 0
1
2
3
2
3
# 错误捕获与降级
if ! command_that_might_fail; then
echo "命令执行失败" >&2
exit 1
fi
error_output=$(command 2>&1) # 捕获 stdout+stderr
result=$(command 2>/dev/null) || result="default" # 忽略错误并降级
command 2>> error.log # 错误追加到日志
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 信号与清理
temp_file=$(mktemp) || exit 1
cleanup() {
local status=$?
trap - EXIT
rm -f "$temp_file" || printf '%s\n' '清理临时文件失败' >&2
exit "$status" # 保留业务失败,不能用 exit 0 掩盖
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 作业控制
command & # 后台运行
jobs -l # 列出后台任务
fg %1 # 移到前台
bg %1 # 前台任务转后台继续
kill %1 # 杀死任务
1
2
3
4
5
2
3
4
5
# 退出状态与严格模式边界
| 情况 | 状态语义与处理 |
|---|---|
| 普通命令 | 通常 0 成功;非零含义由命令定义,立即保存 $? |
grep | 0 匹配、1 无匹配、2 错误;不能把无匹配当 I/O 故障 |
diff / cmp | 0 相同、1 不同、diff 为 2/cmp 大于 1 表示错误;见 比较指南 |
! command | 将状态取反;if ! command; then rc=$? 得到的是取反后的 0 |
((count++)) | 表达式旧值为 0 时状态为 1;现代 Bash 可因此触发 -e,macOS Bash 3.2 行为有差异;计数用 count=$((count + 1)) |
| 默认管道 | 通常仅取最后一段状态;pipefail 改变状态,不代表管道立即停止 |
-e 在 if/while/until 条件、!、部分 &&/||、非末段管道等位置受到抑制;函数作为条件或出现在 func || handler 时,函数体内的 -e 也可能被忽略。命令替换中的继承还受 POSIX 模式、inherit_errexit 与版本影响。可复用函数对 mkdir、复制、写文件等关键操作应显式检查,不依赖调用方的选项。
if operation; then
printf '%s\n' '成功'
else
status=$? # 立即保存真正的失败码
printf '失败状态: %s\n' "$status" >&2
exit "$status"
fi
1
2
3
4
5
6
7
2
3
4
5
6
7
set -u 下可用 ${optional:-} 访问可选参数。全局 IFS=$'\n\t' 只改变分词规则,也会改变 $* 的连接行为;局部 IFS= read -r、变量引用及 NUL 分隔才是针对输入的处理方式。tr ... /dev/urandom | head -c N 可能因上游 SIGPIPE 返回 141;有限输入实现见 随机串函数。
# 调试与验证
| 命令 | 检查范围 |
|---|---|
bash -n script.sh | 仅检查语法,不执行;使用目标 Bash 版本 |
shellcheck script.sh | 静态检查引用、分词等问题,需另行安装 |
bash -x script.sh / set -x、set +x | 执行并记录展开后的命令;日志可能包含凭据,不将原始日志直接公开 |
trap 'printf "line=%s status=%s\n" "$LINENO" "$?" >&2' ERR | 辅助定位,ERR 与 -e 有相似例外;函数继承需考虑 set -E |
printf '%s\n' "${PIPESTATUS[@]}" | Bash 管道各段状态,必须紧接管道;后续命令会覆盖它 |
先用临时目录验证正常、空输入、空格/换行文件名、目标冲突和故障状态。用模拟命令测试复制失败,避免触碰真实配置。作业控制属于当前交互 Shell,非交互脚本默认没有同样的控制终端/作业设置。
依据:Bash set 规则 (opens new window)、Bash 管道 (opens new window)。
上次更新: 2026/10/26, 22:32:55