5.5.3 调试方法与排错工具
调试方法与排错工具#
调试的目标是快速定位问题、复现异常并验证修复。建议采用“最小化复现 → 增加可观测性 → 定位根因 → 验证修复”的闭环流程,并配合系统级工具。
调试流程原理草图
1) 基础调试手段与严格模式
- 执行跟踪:输出每行实际执行的命令,快速定位出错行。
- 严格模式:在错误、未定义变量或管道失败时立即退出。
示例脚本 /opt/scripts/demo_debug.sh:
#!/usr/bin/env bash
set -euo pipefail
set -x
input="${1:-/etc/hosts}"
grep -n "localhost" "$input" | awk '{print $1":"$2}'
运行与预期:
bash -x /opt/scripts/demo_debug.sh /etc/hosts
# 预期:输出执行的每一步命令,并打印匹配到的行号和字段
命令解释
- set -e:任意命令失败即退出
- set -u:未定义变量即退出
- set -o pipefail:管道中任一命令失败即整体失败
- set -x:打印实际执行的命令(含展开后的变量)
2) 局部开启/关闭跟踪(断点调试)
set -x
do_step_1
set +x
do_step_2
用途:只跟踪关键步骤,避免大量输出干扰。
3) 典型错误定位与示例
变量展开异常
# 错误示例:变量为空导致路径错误
path="/data/$env/app"
ls "$path"
修复示例:
env="${env:-prod}"
path="/data/${env}/app"
ls "$path"
管道错误丢失
# 修复前:grep失败但脚本继续
set -e
cat /tmp/no_such_file | grep "abc"
修复后:
set -euo pipefail
cat /tmp/no_such_file | grep "abc"
条件判断错误
# 错误:字符串比较用 -eq
if [ "$mode" -eq "prod" ]; then
echo "prod"
fi
正确:
if [ "$mode" = "prod" ]; then
echo "prod"
fi
4) 常用排错工具(含安装与命令解释)
4.1 shellcheck(静态检查)
安装:
# Ubuntu/Debian
sudo apt-get update && sudo apt-get install -y shellcheck
# CentOS/RHEL
sudo yum install -y epel-release
sudo yum install -y shellcheck
使用:
shellcheck /opt/scripts/demo_debug.sh
预期:输出语法与潜在错误提示行号。
4.2 bash -n(语法检查)
bash -n /opt/scripts/demo_debug.sh
# 无输出表示语法正确
4.3 strace(系统调用跟踪)
安装:
# Ubuntu/Debian
sudo apt-get install -y strace
# CentOS/RHEL
sudo yum install -y strace
使用:
strace -f -o /tmp/trace.log bash /opt/scripts/demo_debug.sh
命令解释:
- -f 跟踪子进程
- -o 输出到文件
排错示例:定位文件不存在
grep -n "ENOENT" /tmp/trace.log | head
4.4 lsof / fuser(端口与文件占用)
sudo lsof -i :8080
sudo fuser -v 8080/tcp
4.5 ps / top / htop(进程状态)
ps -ef | grep demo_debug.sh
top -p $(pgrep -f demo_debug.sh)
5) 统一日志与错误捕获示例
示例脚本 /opt/scripts/demo_log.sh:
#!/usr/bin/env bash
set -euo pipefail
log() {
printf '%s [%s] (%s:%s) %s\n' "$(date +%F\ %T)" "$1" "${BASH_SOURCE[0]}" "$LINENO" "$2"
}
trap 'log "ERROR" "exit code $? at line $LINENO"' ERR
log "INFO" "start"
cp /tmp/not_exist /tmp/backup || log "WARN" "copy failed"
log "INFO" "end"
运行:
bash /opt/scripts/demo_log.sh
# 预期:输出统一格式日志,并捕获错误行号
6) 排错流程完整示例(从复现到修复)
问题:脚本读取配置失败,提示“文件不存在”。
步骤:
# 1) 最小化复现
bash -x /opt/scripts/load_cfg.sh /etc/app/config.yaml
# 2) 语法与静态检查
bash -n /opt/scripts/load_cfg.sh
shellcheck /opt/scripts/load_cfg.sh
# 3) 系统级验证
strace -f -o /tmp/trace.log bash /opt/scripts/load_cfg.sh /etc/app/config.yaml
grep -n "ENOENT" /tmp/trace.log | head
# 4) 验证修复(路径修正后回归)
bash /opt/scripts/load_cfg.sh /etc/app/config.yaml
7) 常见排错清单(速查)
- shebang 是否正确:#!/usr/bin/env bash
- 执行权限:chmod +x script.sh
- 环境变量:env | grep VAR
- PATH 是否包含外部命令路径
- 临时文件冲突:mktemp 替代固定文件名
8) 练习题(含预期方向)
-
编写脚本读取传入文件并统计行数,要求开启
set -euo pipefail,并使用trap捕获错误。
- 预期:文件不存在时输出 ERROR 日志并退出。 -
使用
shellcheck检查以下脚本并修复:
#!/bin/bash
if [ $1 -eq "prod" ]; then
echo ok
fi
- 预期:修复字符串比较错误与未引用变量问题。
- 使用
strace找出脚本调用了哪些配置文件路径。
- 预期:在trace.log中找到openat相关记录。