返回 Bug 清单
BUG-0217project-12中已修复deploy-script

错误捕获钩子用 trap ERR + ${BASH_COMMAND} —— 记录到的是 trap 自身的命令

发现:2026-10-01验证:2026-10-27置信度:97%

症状

一个用于记录「哪条命令失败了」的 bash 钩子,产物里大量出现自指记录:记录的命令内容是 local __cmd="${BASH_COMMAND:-unknown}"、__cmd="${BASH_COMMAND:-unknown}" 等 —— 那正是钩子自身的语句。实测 5 条记录里 3 条是这种噪声(60%)。

根因

两个缺陷叠加: ① ${BASH_COMMAND} 在 ERR trap 【内部】求值时,返回的是【当前正在执行的命令】—— 也就是 trap 函数体里的语句,而不是触发 trap 的那条失败命令; ② `DBUG_IN_HOOK=1 __dbug_write …` 这种「前缀赋值 + 命令」的形式, 拦不住 __dbug_on_err 自身内部语句失败时【再次进入 trap】(那时变量还没设上)。

修复方案

改用 PROMPT_COMMAND(在命令执行完、显示提示符前运行一次的函数): · $? 语义正确(就是上一条命令的退出码) · 用 history 1 取最后执行的命令,不涉及 trap 内部命令 · 它不是信号陷阱,天然不会递归 · 与 PowerShell 的 prompt 函数同构 ⇒ 两侧行为一致 另需注意: · history 行形如 " 123 cmd",解析要用锚定正则 ^[[:space:]]*[0-9]+[[:space:]]+(.*)$(用 ${var#*[0-9]} 对多位序号与 " 8 cmd" 会解析错误); · 引用环境变量一律用 ${VAR:-} —— 否则用户 shell 开了 set -u 时钩子会报 "VAR: unbound variable",违反其「绝不影响主 shell」的承诺; · 挂载要幂等,并保留既有的 PROMPT_COMMAND 内容。

检测命令

grep -cE '__dbug|BASH_COMMAND' ~/.dbug/errors.jsonl   # 自指记录数

修复命令

# 用 PROMPT_COMMAND 替代 trap ERR
__dbug_on_prompt() {
  local __ec=$?
  [ "$__ec" -eq 0 ] && return 0
  [ -n "${DBUG_HOOK_OFF:-}" ] && return 0
  local __raw; __raw="$(HISTTIMEFORMAT= builtin history 1 2>/dev/null)"
  if [[ "$__raw" =~ ^[[:space:]]*[0-9]+[[:space:]]+(.*)$ ]]; then __cmd="${BASH_REMATCH[1]}"; fi
  ...
}
PROMPT_COMMAND="__dbug_on_prompt${PROMPT_COMMAND:+;$PROMPT_COMMAND}"

错误签名

{"command":"local __cmd=\"${BASH_COMMAND:-unknown}\""} —— 记录的"失败命令"是钩子内部代码

技术信息

来源项目

project-12

标签

bash, trap, err-trap, prompt-command, hook...