当前位置:  开发笔记 > 编程语言 > 正文

当我的shell脚本退出时,如何终止后台进程/作业?

如何解决《当我的shell脚本退出时,如何终止后台进程/作业?》经验,为你挑选了6个好方法。

当我的顶级脚本退出时,我正在寻找一种清理混乱的方法.

特别是如果我想使用set -e,我希望后台进程会在脚本退出时死掉.



1> Johannes Sch..:

要清理一些乱七八糟的东西,trap可以使用.它可以提供特定信号到达时执行的内容列表:

trap "echo hello" SIGINT

但是如果shell退出,也可以用来执行某些操作:

trap "killall background" EXIT

它是内置的,因此help trap会为您提供信息(与bash一起使用).如果你只想杀死后台工作,你可以这样做

trap 'kill $(jobs -p)' EXIT

注意使用单一',以防止外壳$()立即替换.


基拉尔杀了你的孩子,但不是你
`kill $(jobs -p)`在dash中不起作用,因为它在子shell中执行命令替换(参见man dash中的命令替换)
是“ killall background”占位符吗?`background`不在手册页中...

2> tokland..:

这对我有用(感谢评论者的改进):

trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT

kill -- -$$将SIGTERM发送到整个进程组,从而杀死后代.

EXIT使用时指定信号很有用set -e(这里有更多细节).


注意,"kill 0"也会杀死父bash脚本.您可能希望使用"kill - - $ BASHPID"来仅杀死当前脚本的子项.如果您的bash版本中没有$ BASHPID,则可以导出BASHPID = $(sh -c'echo $ PPID')
@EvanBenn:选中“ man 2 kill”,这说明当PID为负时,信号将以提供的ID(https://en.wikipedia.org/wiki/Process_group)发送到进程组中的所有进程。令人困惑的是,在“ man 1 kill”或“ man bash”中未提及此问题,并且在文档中将其视为错误。
感谢您提供清晰明了的解决方案!不幸的是,它突然出现Bash 4.3,它允许陷阱递归.我在OSX上的`4.3.30(1)-release`上遇到了这个问题,它也是[在Ubuntu上确认](https://bugs.launchpad.net/ubuntu/+source/bash/+bug/1337827) .有一个[obvoius wokaround](http://stackoverflow.com/a/28333938/804678),但:)
我不太了解`-$$`。评估结果为“-<PID>”,例如“ -1234”。在kill联机帮助页//内置的联机帮助页中,前导破折号指定要发送的信号。但是-可能阻止了该操作,但是如果没有,则前导破折号没有记载。有什么帮助吗?

3> korkman..:

更新:https://stackoverflow.com/a/53714583/302079通过添加退出状态和清除功能来改进此功能.

trap "exit" INT TERM
trap "kill 0" EXIT

为什么要转换INTTERM退出?因为两者都应该触发kill 0而不进入无限循环.

为什么引发kill 0EXIT?因为正常的脚本退出也应该触发kill 0.

为什么kill 0?因为嵌套的子壳也需要被杀死.这将取消整个流程树.


这很棒,但也会杀死我的父shell :-(
这个解决方案确实有点矫枉过正.kill 0(在我的脚本中)破坏了我的整个X会话!也许在某些情况下,kill 0可能是有用的,但这不会改变它不是一般解决方案的事实,如果可能的话应该避免,除非有充分的理由使用它.最好添加一个警告,它可能会杀死父shell甚至整个X会话,而不仅仅是脚本的后台作业!
我在Debian上的唯一解决方案.
Johannes Schaub的答案和tokland提供的答案都没有设法杀死我的shell脚本启动的后台进程(在Debian上).该解决方案有效.我不知道为什么这个答案没有更多的支持.你能进一步扩展一下`kill 0`的意思吗?
@josch如果你还没发现,[这里是'kill 0`的解释](http://unix.stackexchange.com/a/67552/23316)
尽管在某些情况下这可能是一个有趣的解决方案,但@vidstige指出,这将杀死“整个进程组”,其中包括启动进程(在大多数情况下,即父外壳程序)。通过IDE运行脚本时,绝对不是您想要的东西。

4> 小智..:

陷阱'kill $(jobs -p)'退出

我只会对Johannes的回答进行微小的更改,并使用jobs -pr将kill限制为正在运行的进程,并向列表中添加更多信号:

trap 'kill $(jobs -pr)' SIGINT SIGTERM EXIT


这比我接受的答案更好.

5> skozin..:

trap 'kill 0' SIGINT SIGTERM EXIT在@ tokland的答案中描述的解决方案非常好,但是最新的Bash 在使用时崩溃并出现了分段错误.这是因为Bash从v.4.3开始,允许陷阱递归,在这种情况下变为无限:

    壳进程临危SIGINTSIGTERMEXIT;

    信号被捕获,执行kill 0,发送SIGTERM到组中的所有进程,包括shell本身;

    去1 :)

这可以通过手动取消注册陷阱来解决:

trap 'trap - SIGTERM && kill 0' SIGINT SIGTERM EXIT

更奇特的方式,允许打印收到的信号,并避免"终止:"消息:

#!/usr/bin/env bash

trap_with_arg() { # from /sf/ask/17360801/
  local func="$1"; shift
  for sig in "$@"; do
    trap "$func $sig" "$sig"
  done
}

stop() {
  trap - SIGINT EXIT
  printf '\n%s\n' "recieved $1, killing children"
  kill -s SIGINT 0
}

trap_with_arg 'stop' EXIT SIGINT SIGTERM SIGHUP

{ i=0; while (( ++i )); do sleep 0.5 && echo "a: $i"; done } &
{ i=0; while (( ++i )); do sleep 0.6 && echo "b: $i"; done } &

while true; do read; done

UPD:添加了最小的例子; 改进了stop功能以避免去除不必要的信号并隐藏输出中的"已终止:"消息.感谢Trevor Boyd Smith的建议!



6> 小智..:

为了安全起见,我发现最好定义一个清理函数并从陷阱中调用它:

cleanup() {
        local pids=$(jobs -pr)
        [ -n "$pids" ] && kill $pids
}
trap "cleanup" INT QUIT TERM EXIT [...]

或者完全避免这个功能:

trap '[ -n "$(jobs -pr)" ] && kill $(jobs -pr)' INT QUIT TERM EXIT [...]

为什么?因为只需使用trap 'kill $(jobs -pr)' [...]一个假定存在是当陷阱条件获得信号,运行后台作业.当没有工作时,人们会看到以下(或类似)消息:

kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]

因为jobs -pr是空的 - 我结束了'陷阱'(双关语).

推荐阅读
mobiledu2402851377
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有