首页 > 解决方案 > 为什么 DEBUG 陷阱的执行次数比预期的要多?

问题描述

以下代码触发了 3 次调试陷阱。然而,我预计只有 2 次处决 - 一次执行foo,第二次执行echo hello

foo() {
    echo hello
}

set -T
trap 'echo oops' DEBUG
foo

输出:

oops
oops
oops
hello

预期输出:

oops
oops
hello

Bash测试版本:

GNU bash, version 4.3.30(1)-release (x86_64-unknown-linux-gnu)
GNU bash, version 4.4.19(1)-release (x86_64-pc-linux-gnu)
GNU bash, version 5.0.0(1)-alpha (x86_64-pc-linux-gnu)

我很确定我只是误解了手册,并且我在这里遗漏了一些非常简单/明显的东西。

标签: bash

解决方案


tl;dr:Bash 在调用函数时会额外运行一次调试陷阱,以允许中断函数本身而不是其第一个命令。以下是用于调用 bash 函数的源代码的execute_function :

  /* Run the debug trap here so we can trap at the start of a function's
     execution rather than the execution of the body's first command. */

源潜水时间

这是执行调试陷阱的代码:

int run_debug_trap () {
/* (...) */
    trap_exit_value = _run_trap_internal (DEBUG_TRAP, "debug trap");

到今天为止,我在该行上放了一个断点trap.c:1081,并查看了回溯:

第一个例子

Breakpoint 1, run_debug_trap () at trap.c:1081
1081          trap_exit_value = _run_trap_internal (DEBUG_TRAP, "debug trap");
(gdb) where
#0  run_debug_trap () at trap.c:1081
#1  0x000055555559fd3d in execute_simple_command (simple_command=0x5555558aacc8, pipe_in=-1, pipe_out=-1, async=0, fds_to_close=0x555555899148) at execute_cmd.c:4056
#2  0x0000555555599fd7 in execute_command_internal (command=0x5555558aae08, asynchronous=0, pipe_in=-1, pipe_out=-1, fds_to_close=0x555555899148) at execute_cmd.c:807
#3  0x00005555555995c1 in execute_command (command=0x5555558aae08) at execute_cmd.c:405
#4  0x0000555555583c9e in reader_loop () at eval.c:180
#5  0x0000555555581794 in main (argc=2, argv=0x7fffffffe4d8, env=0x7fffffffe4f0) at shell.c:792
(gdb) up
#1  0x000055555559fd3d in execute_simple_command (simple_command=0x5555558aacc8, pipe_in=-1, pipe_out=-1, async=0, fds_to_close=0x555555899148) at execute_cmd.c:4056
4056      result = run_debug_trap ();

(gdb) print *simple_command->words->word
$3 = {word = 0x5555558a5268 "foo", flags = 0}

换句话说,这是一个简单的命令foo。到现在为止还挺好。

第二审

Breakpoint 1, run_debug_trap () at trap.c:1081
1081          trap_exit_value = _run_trap_internal (DEBUG_TRAP, "debug trap");
(gdb) where
#0  run_debug_trap () at trap.c:1081
#1  0x00005555555a170a in execute_function (var=0x5555558ab648, words=0x5555558aac28, flags=0, fds_to_close=0x555555899148, async=0, subshell=0) at execute_cmd.c:4787
#2  0x00005555555a1c68 in execute_builtin_or_function (words=0x5555558aac28, builtin=0x0, var=0x5555558ab648, redirects=0x0, fds_to_close=0x555555899148, flags=0) at execute_cmd.c:5030
#3  0x00005555555a0660 in execute_simple_command (simple_command=0x5555558aacc8, pipe_in=-1, pipe_out=-1, async=0, fds_to_close=0x555555899148) at execute_cmd.c:4330
#4  0x0000555555599fd7 in execute_command_internal (command=0x5555558aae08, asynchronous=0, pipe_in=-1, pipe_out=-1, fds_to_close=0x555555899148) at execute_cmd.c:807
#5  0x00005555555995c1 in execute_command (command=0x5555558aae08) at execute_cmd.c:405
#6  0x0000555555583c9e in reader_loop () at eval.c:180
#7  0x0000555555581794 in main (argc=2, argv=0x7fffffffe4d8, env=0x7fffffffe4f0) at shell.c:792

这更有趣。我们仍在执行foo之前的简单命令,但它又触发了!为什么呢?让我们看一下调用站点execute_cmd.c:4787

  /* Run the debug trap here so we can trap at the start of a function's
     execution rather than the execution of the body's first command. */
  showing_function_line = 1;
  save_current = currently_executing_command;
  result = run_debug_trap ();

换句话说,Bash 似乎故意再运行一次调试陷阱,目的是为了中断一个函数而不是它的第一个命令。

三审

不出所料是函数的echo命令,所以我不会包括它。


推荐阅读