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) 练习题(含预期方向)

  1. 编写脚本读取传入文件并统计行数,要求开启 set -euo pipefail,并使用 trap 捕获错误。
    - 预期:文件不存在时输出 ERROR 日志并退出。

  2. 使用 shellcheck 检查以下脚本并修复:

#!/bin/bash
if [ $1 -eq "prod" ]; then
  echo ok
fi
  • 预期:修复字符串比较错误与未引用变量问题。
  1. 使用 strace 找出脚本调用了哪些配置文件路径。
    - 预期:在 trace.log 中找到 openat 相关记录。