阻止具有后台进程的子 shell 接收 SIGINT,尽管在前台捕获信号

Stop subshell with background process from receiving SIGINT despite trapping signals in the foreground

提问人:Gigi Bayte 2 提问时间:2/27/2023 更新时间:2/28/2023 访问量:98

问:

假设我有一个在前台运行的函数。此函数捕获 SIGINT 并忽略 EOF(用于防止 Control + C 和 Control + D)。此函数创建一个在后台运行命令的子 shell。

我认为 SIGINT 会被主功能捕获。但是,在运行 main 函数时使用 Control + C 仍会导致子 shell 接收 SIGINT 并在预期之前被杀死。

我还尝试为 SIGINT 添加陷阱并忽略子 shell 本身中的 EOF,但这似乎也不起作用。

下面是一个相对最小的示例,它使用 mpv 命令封装了问题:

function play_video_until_yes {
    trap '' 2
    set -o ignoreeof
    
    videourl="$1"
    read -r mpv_pid < <(mpv --loop "$videourl" --no-video &>/dev/null & echo $!)
    
    while true; do
        read -rp "Input y for yes: " answer
        if [[ "$answer" = "y" ]]; then
            break
        fi
        printf "\nIncorrect. Try again.\n"
    done
    
    kill "$mpv_pid" >/dev/null
    
    trap 2
    set +o ignoreeof
}

可以使用任何 YouTube 视频的命令行参数(例如)运行此函数,并在主进程要求用户输入时按 Control + C。这会导致子 shell 退出,可能是由于 SIGINT。play_video_until_yes "https://www.youtube.com/watch?v=usNsCeOV4GM"

bash shell 信号 sigint subshell

评论


答:

0赞 Gigi Bayte 2 2/28/2023 #1

我对此做了相当多的研究,并能够找到一个答案。它有效,但不太理想。

我必须在我的 Mac 上安装一个名为 via Homebrew 的单独软件包才能获得该命令。然后,我可以使用该命令在单独的进程组中运行该命令,以防止将 SIGINT 转发到它。util-linuxsetsidmpv

所以,这是我的解决方案之后的样子:

function play_video_until_yes {
    trap '' 2
    set -o ignoreeof

    videourl="$1"
    read -r mpv_pid < <( /opt/homebrew/opt/util-linux/bin/setsid mpv --loop "$videourl" --no-video &>/dev/null & echo $!)

    while true; do
        read -rp "Input y for yes: " answer
        if [[ "$answer" = "y" ]]; then
            break
        fi
        printf "\nIncorrect. Try again.\n"
    done

    kill "$mpv_pid" >/dev/null

    trap 2
    set +o ignoreeof
}

我仍然更喜欢使用macOS上已经存在的命令的解决方案,但这个解决方案目前仍然站稳脚跟。