【问题标题】:Terminate running commands when shell script is killed [duplicate]终止 shell 脚本时终止正在运行的命令 [重复]
【发布时间】:2010-12-11 07:39:33
【问题描述】:

出于测试目的,我有这个 shell 脚本

#!/bin/bash
echo $$
find / >/dev/null 2>&1

从交互式终端运行此命令,ctrl+c 将终止 bash 和 find 命令。

$ ./test-k.sh
13227
<Ctrl+C>
$ ps -ef |grep find
$

在后台运行它并杀死 shell 只会孤立脚本中运行的命令。

$ ./test-k.sh &
[1] 13231
13231
$ kill 13231
$ ps -ef |grep find
nos 13232     1  3 17:09 pts/5    00:00:00 find /
$

我希望这个 shell 脚本在它退出时终止它的所有子进程,不管它是如何被调用的。它最终将从 python 和 java 应用程序启动 - 脚本退出时需要某种形式的清理 - 我应该研究的任何选项或重写脚本以在退出时自行清理的任何方式?

【问题讨论】:

    标签: bash


    【解决方案1】:

    我会这样做:

    #!/bin/bash
    trap : SIGTERM SIGINT
    
    echo $$
    
    find / >/dev/null 2>&1 &
    FIND_PID=$!
    
    wait $FIND_PID
    
    if [[ $? -gt 128 ]]
    then
        kill $FIND_PID
    fi
    

    我想,有些解释是有序的。出门在外,我们需要更改一些默认的信号处理。 : 是一个无操作命令,因为传递一个空字符串会导致 shell 忽略该信号,而不是对其进行处理(与我们想要做的相反)。

    然后,find 命令在后台运行(从脚本的角度来看),我们调用 wait 内置函数来完成它。由于我们在上面给trap 发出了真正的命令,所以当处理一个信号时,wait 将以大于 128 的状态退出。如果进程 waited for 完成,wait 将返回该退出状态过程。

    最后,如果wait 返回那个错误状态,我们想要kill 子进程。幸运的是,我们保存了它的 PID。这种方法的优点是您可以记录一些错误消息或以其他方式识别信号导致脚本退出。

    正如其他人所提到的,如果您不关心在退出后留下任何信息,则将 kill -- -$$ 作为您对 trap 的参数是另一种选择。

    要使trap 以您想要的方式工作,您确实需要将其与wait 配对-bash 手册页显示“如果bash 正在等待命令完成并接收到信号trap 已设置,trap 在命令完成之前不会执行。” wait 是解决这个问题的方法。

    如果需要,您也可以将其扩展到更多子进程。我并没有真正彻底地测试这个,但它似乎在这里工作。

    $ ./test-k.sh &
    [1] 12810
    12810
    $ kill 12810
    $ ps -ef | grep find
    $
    

    【讨论】:

    • 常规的no-op命令是“:”。
    • 如果我在后台启动进程,那么它不会被杀死。如果我不在后台启动它,生成的子进程确实会被杀死
    • 当心这个缺点(以及我发现的大多数其他方法):如果你 kill -9 父级不会执行清理。作为一种解决方法,在某些情况下,您可以让孩子监控其父母的健康状况。
    【解决方案2】:

    正在寻找一个优雅的解决方案来解决这个问题,并在其他地方找到了以下解决方案。

    trap 'kill -HUP 0' EXIT
    

    我自己的手册页没有说明0 的含义,但从四处挖掘,它似乎意味着当前的进程组。由于脚本拥有自己的进程组,因此最终会向脚本的所有子进程、前台和后台发送 SIGHUP。

    【讨论】:

    • 这似乎工作得很好(尝试用 ctrl-C 杀死脚本并关闭终端)但我害怕隐藏的缺点。有没有 bash 大师愿意分享他的观点?
    • stackoverflow.com/a/22644006/301717 提出了类似的答案,但似乎更强大
    【解决方案3】:

    向群组发送信号。 所以代替kill 13231 做:

    kill -- -13231
    

    如果您是从 python 开始,请查看: http://www.pixelbeat.org/libs/subProcess.py 它显示了如何在启动时模仿 shell 杀死一群人

    【讨论】:

    • 这个脚本中的每个命令都属于那个进程组吗?
    • 如果是从 shell 启动,是的,否则你需要像在 subProcess.py 中那样启动
    • 您并不总是能够控制脚本的终止方式
    • 这应该是公认的答案。
    【解决方案4】:

    @Patrick 的回答几乎成功了,但如果您的 current shell 的 parent 进程在同一个组中(它也会杀死父进程),则它不起作用)。

    我发现这样更好:

    trap 'pkill -P $$' EXIT

    请参阅here 了解更多信息。

    【讨论】:

      【解决方案5】:

      只需在脚本中添加这样的一行:

      trap "kill $$" SIGINT
      

      您可能需要在设置中将“SIGINT”更改为“INT”,但这基本上会在您按下 Ctrl-C 时杀死您的进程和所有子进程。

      【讨论】:

      • 而不是 'kill $$',您可能应该将 TERM 发送到 find 命令的 pid!
      • $! 也许,但您可能想要比这更精细的控制。
      【解决方案6】:

      您需要做的是捕获 kill 信号,杀死 find 命令并退出。

      【讨论】:

      • kill 默认发送SIGTERMSIGKILL 无法捕获。
      猜你喜欢
      • 2016-10-01
      • 1970-01-01
      • 2016-04-18
      • 2012-10-24
      • 2018-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多