首页 > 解决方案 > bash SIGINT 陷阱触发一次但不再触发

问题描述

我需要帮助了解 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 信号时,如何让陷阱响应?

标签: bashsignalschild-processsigintbash-trap

解决方案


我不确定,但我认为这是因为menu第二次调用时您仍处于“陷阱处理程序”中。因为仍在处理一个信号,所以不会处理第二个信号。如果您要删除该调用并用 包裹menuwhile 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 中,陷阱也将起作用。


推荐阅读