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 -vset -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 开跟踪,平时关掉不影响性能。

以上,够用了。遇到具体问题再来问。

📚 推荐资源

– 部分链接含推广返佣 –

🪐 加入「渗透实战安全圈」

每天分享渗透测试实战、挖洞技巧、漏洞分析、工具推荐

知识星球

https://t.zsxq.com/40MyD

💻 安全运维 / Linux运维 / 渗透测试 技术支持
业务需求可联系博客作者

By admin

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注