为什么在从脚本启动的应用程序中按 Control + C 会破坏脚本?

为什么在从脚本启动的应用程序中按 Control + C 会破坏脚本?

直接进入:

while true; do
    #---  MENU LOGIC HERE, stick response in $MENUEXIT

    #----Deal with responses here

    if [ $S1 == $MENUEXIT ];
    then
            tail -f /path-to-file
    else
            sleep 2
    fi
done

我已尝试使其尽可能简单,如果您需要更多信息,请告诉我。

基本上,我将菜单置于循环中,因此,如果用户输入不正确或命令完成,则意味着重新显示菜单。

一切都运行正常,直到我添加了 tail 命令。

如果我从菜单中选择 tail 选项,tail 命令就会正常启动 - 但是,如果我点击Ctrl+ C,我希望 tail 终止并且菜单显示出来,但它却同时终止 tail剧本。

我尝试了各种各样的方法,比如继续/陷阱等等,但是,我遇到了障碍,需要一些帮助吗?

答案1

当你发送如下信号时SIGINT( CtrlC) 或SIGSTOP( CtrlZ)从终端,信号被发送到前台进程组。也就是说,由前台作业(您的脚本)及其在前台的任何子进程(命令tail)组成的组。这会导致所有这些进程退出(或通过处理信号来执行它们所做的任何操作)。您可以通过使用以下命令发送信号来测试差异kill

在一个终端中,执行此脚本(我test在以下命令中调用它):

#! /bin/bash

while true
do
    if [[ $1 = a ]]
    then
        yes 
    else
        echo no
        sleep 2
    fi
done

在另一个文件中执行以下命令:

pstree -ps $(pgrep -f test)
pkill -f test
pstree -ps $(pgrep -f yes)

命令的输出将会像这样:

init(1)───lightdm(1032)───lightdm(1154)───init(1173)───x-terminal-emul(17009)───bash(24262)───test.sh(24996)───yes(24997)
# No output for pkill
init(1)───lightdm(1032)───lightdm(1154)───init(1173)───yes(24997)

(实际数字和程序可能会有所不同,但第一个 init 总是会有进程 1。)
如您所见,该yes命令没有被杀死,而是被附加到,init因为它的父级被杀死了。它仍然y在第一个终端上愉快地打印 s。因此使用 杀死它pkill -f yes。现在重复实验,但有一处变化。而不是pkill -f test,而是:

kill -INT -25165 
# Use the pid of test.sh given in parentheses in the output of pstree 
# instead of 25165

注意前面的-。在 Linux 中,对于该kill命令,-25165是进程组,其领导者具有 pid 25165。因此,此命令相当于从终端发送中断。

当然,具体行为取决于 TTY、登录 shell 等的配置。这是我的理解。进一步阅读:


我建议:

  1. 发送tail到后台
  2. 捕获信号
  3. 终止后台进程。
  4. 移除陷阱

一个例子:

#! /bin/bash

kill_jobs ()
{
    kill %1
}

while true
do
    if [[ $1 = "a" ]]
    then
        trap kill_jobs INT
        tail -f /var/log/syslog &
        wait %%
        shift #You don't need to shift, I just didn't want loop tail on this example
        trap - INT
    else
        echo some output
        sleep 2
    fi
done

通过使用不同的函数进行捕获,并分别执行自己的清理工作,它可以变得非常强大。

相关内容