Shell 脚本调试技巧汇总
调试 Shell 脚本经常让人头大,尤其是那些跑了好几年、变量满天飞的祖传脚本。下面列几个我常用的调试方法,直接上命令和场景。
1. 开启调试模式
最粗暴但最有效:bash -x 或脚本内加 set -x
# 执行时看每一步
bash -x your_script.sh
# 脚本内局部调试
set -x # 开启跟踪
# ... 需要调试的代码块 ...
set +x # 关闭跟踪
输出格式:+ 命令 前面带 + 的就是实际执行的命令,变量会被展开。
2. 只检查语法不执行
bash -n your_script.sh
# 没输出说明语法没问题,有错误会直接报错
适合 CI/CD 里加一道检查。
3. 显示未展开的变量
set -v 比 set -x 更原始,它打印原始行,变量不展开:
bash -v your_script.sh
# 或者 set -v
4. 打印行号
调试大脚本时不知道错误在哪行?加 PS4 变量:
export PS4='+${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]}: '
bash -x your_script.sh
输出变成:+script.sh:42:main: echo $VAR 一目了然。
5. 捕获错误立即退出
set -e # 任何命令失败就退出
set -u # 使用未定义变量就报错
set -o pipefail # 管道中任何一个命令失败都算失败
推荐组合:set -euo pipefail,写在脚本第二行(第一行是 shebang)。
6. 自定义调试函数
不想全局 set -x 输出太乱?写个 debug 函数:
debug() {
[[ "$DEBUG" == "1" ]] && echo "[DEBUG] $(date +%H:%M:%S) $*" >&2
}
# 使用
debug "当前变量 VAR=$VAR"
调用时 DEBUG=1 ./script.sh 开启调试。
7. 检查退出码
command
echo "退出码: $?"
# 或者一行搞定
command || { echo "失败: $?"; exit 1; }
8. 陷阱(trap)调试
# 任何错误时打印行号
trap 'echo "错误发生在第 $LINENO 行, 退出码=$?" >&2' ERR
# 脚本退出时清理
trap 'rm -f /tmp/tmpfile; echo "清理完成"' EXIT
9. 输出重定向分离
把调试信息单独输出到文件,不影响正常输出:
exec 3>&1 # 保存标准输出
exec > >(tee -a /var/log/script.log) # 正常日志
exec 2> >(tee -a /var/log/script.err >&2) # 错误日志
# 调试信息走 stderr
echo "调试信息" >&2
10. 终极武器:shellcheck
# 安装
apt install shellcheck # Debian/Ubuntu
brew install shellcheck # macOS
# 使用
shellcheck your_script.sh
能查出未引用的变量、常见的语法陷阱、可移植性问题。建议写脚本时开个编辑器插件实时检查。
实战组合拳
写新脚本时我一般这么开头:
#!/bin/bash
set -euo pipefail
[[ "$TRACE" == "1" ]] && set -x
# 调试信息
debug() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >&2; }
# 错误处理
error_handler() {
local line=$1
local exit_code=$2
debug "错误: 第 ${line} 行, 退出码 ${exit_code}"
}
trap 'error_handler ${LINENO} $?' ERR
调试时 TRACE=1 ./script.sh 开跟踪,平时关掉不影响性能。
以上,够用了。遇到具体问题再来问。
💻 安全运维 / Linux运维 / 渗透测试 技术支持
业务需求可联系博客作者
