bash SIGINT 陷阱触发一次但不会再次触发

bash SIGINT trap fires once but never again

我需要帮助来理解 SIGINT 是如何在 shell 脚本的主要流程和该脚本中的函数调用之间传播的。对于我的脚本,我有一个主菜单,它接受用户输入并根据该输入调用子函数。子函数与用户交互。

我的目标如下:

我看到的行为是,我可以在主菜单内或子功能内按 ctrl+c,它第一次会按预期工作,但所有后续的 ctrl+c 信号都将被忽略。

我觉得我的问题与这些非常接近:

尽管在他们的情况下,他们在新进程中调用子进程,而我正在调用源函数,我认为这对 fg/bg 不会有相同的影响,对吗?

举个最简单的例子,假设我有一个名为 main.sh:

的文件
trap catchInt SIGINT
reallyQuit=0
function catchInt(){
    echo "caught sigint"
    if (( $reallyQuit > 0 ));then
        echo "really quitting."
        exit 1
    else
        let reallyQuit++
        echo "reallyquit is now $reallyQuit"
    fi
    menu
}
function menu(){
    read -ep $'Please choose a number and press enter.\n\t' input
    case $input in
        1)
            child
            menu
            ;;
        *)
            echo "Quitting"
            exit 0
            ;;
    esac
}
source child.sh
# I also source other scripts with utility functions and exported vars
menu

如果我在同一目录中有一个名为 child.sh 的文件:

function child(){
        read -p "Please type something"
        # I also use utility functions and exported vars
}

下面是上述代码的示例 运行,我在菜单中按了 ctrl+c,然后在子函数中再次尝试:

bash main.sh
Please choose a number and press enter.
    caught sigint
reallyquit is now 1
Please choose a number and press enter.
    1
Please type something^C^C^C^C^C (I finally pressed enter)
Please choose a number and press enter.
(I tapped ctrl-c a few times here and finally pressed enter)
Quitting

这是一个例子,我先输入 1,然后按 ctrl-c:

bash main.sh
Please choose a number and press enter.
    1
Please type something^Ccaught sigint
reallyquit is now 1
Please choose a number and press enter.
(I tapped ctrl-c a few times here and finally pressed enter)
Quitting

如何让陷阱在每次发送 INT 信号时响应?

我不确定,但我认为这是因为第二次调用 menu 时您仍在 "trap handler" 中。因为仍在处理一个 sigint,所以不会处理第二个 sigint。如果您删除该调用并将 menu 换成 while true; do ...; done,它确实有效:

#! /bin/bash

reallyQuit=0
function catchInt(){
    echo "caught sigint"
    if (( $reallyQuit > 0 ));then
       echo "really quitting."
        exit 1
    else
        let reallyQuit++
        echo "reallyquit is now $reallyQuit"
    fi
}

trap catchInt SIGINT

function menu(){
    read -ep $'Please choose a number and press enter.\n\t' input
    case $input in
        1)
            child
            menu
            ;;
        *)
            echo "Quitting"
            exit 0
            ;;
    esac
}
function child(){
        read -p "Please type something"
        # I also use utility functions and exported vars
}

# I also source other scripts with utility functions and exported vars
while true; do
        menu
done
exit 0

编辑:
为什么要在 main.sh 中包含 child.sh?通常人们会创建通用函数并将其包含在子脚本中。这样您就可以在 child1.sh,..., childN.sh 之间共享 main.sh 中的函数。如果将 source main.sh 添加到 child.sh,陷阱也会起作用。