调试 shell 脚本需可观测、可捕获、可复现:用 set -x 显示实际执行命令及变量展开,配合 ps4 加行号;用 trap 捕获 err 时的行号、退出码和关键环境变量;先语法检查(bash -n)、修复换行符、确认权限与 shebang;再通过日志、状态检查($?)和 set -u 主动暴露问题。

调试 Shell 脚本不靠猜,靠可观测、可捕获、可复现。核心是让脚本“说话”——既告诉你它执行了什么,也告诉你它在哪一步卡住了、为什么卡住。
用 set -x 看清每一步实际执行了什么
这是最直接的跟踪手段:它会把每条真正运行的命令(含变量展开后的结果)打印出来,前面带 + 号。比如 echo $PATH 会展开成 echo /usr/local/bin:/bin:/usr/bin 并显示。
- 在脚本开头加
set -x,全局启用;局部调试时,用set -x开启,set +x关闭 - 配合
PS4='[$LINENO] ',让每行输出带上行号,定位更准 - 敏感信息(如密码、token)会被原样打出,调试完记得删掉或临时注释相关变量
- 输出重定向到文件更方便分析:
./script.sh 2>&1 | tee debug.log,再用grep '^+' debug.log快速过滤执行流
用 trap 捕获错误瞬间的上下文
set -x 告诉你“哪条命令错了”,trap 告诉你“错的时候环境是什么样”。两者配合才能闭环。
- 基础写法:
trap 'echo "ERROR at line $LINENO, exit code: $?"' ERR,命令失败时立刻报行号和退出码 - 实用增强:
trap 'env | grep -E "^(PATH|HOME|MY_VAR)="' > /tmp/err_env.log; echo "Failed at $LINENO" >&2' ERR,只抓关键变量,避免日志爆炸 - 建议搭配
set -e(遇错即停),确保trap ERR不被跳过
先检查语法和执行前提,再深入逻辑
很多“报错”其实根本没走到逻辑层,卡在基础环节。
- 语法检查:
bash -n script.sh,只解析不执行,快速发现括号不匹配、if 缺 fi 等硬伤 - 换行符问题:Windows 编辑的脚本常带
^M,报bad interpreter。用file script.sh查格式,dos2unix script.sh修复 - 权限与调用方式:确认有
x权限(chmod +x script.sh),且用./script.sh运行,而非直接script.sh - shebang 路径:首行
#!/bin/bash中的路径要真实存在,用which bash验证
加日志和状态检查,让问题浮出水面
对关键步骤主动留痕,比等报错后再回溯更高效。
- 简单有效:
echo "[DEBUG] $(date): entering function xyz"或封装成log_debug "entering xyz" - 检查命令是否真成功:
if ! cp file1 file2; then log_error "cp failed"; exit 1; fi,别只信cp写了就一定成 - 关注
$?:上一条命令结束后立即查退出码,0才是成功;[[ $? -eq 0 ]] || { echo "fail"; exit 1; } - 用
set -u让未定义变量直接报错,避免静默出错(如$USER_NAME拼错成$USERNAME)











