2008-12-11 62 views

回答

132

要清理一些烂摊子,可以使用trap。它可以提供的执行功能的列表,当一个特定的信号到达:

trap "echo hello" SIGINT 

,但也可以使用,如果在shell退出执行的东西:

trap "killall background" EXIT 

这是一个内置的,所以help trap会给你信息(与bash一起工作)。如果你只是想杀死后台作业,你可以做

trap 'kill $(jobs -p)' EXIT 

当心使用单一',以防止shell立即替换$()

+0

那么你怎么killall *孩子*只? (或者我错过了一些明显的东西) – elmarco 2008-12-11 20:51:01

+12

killall杀死你的孩子,但不是你 – orip 2008-12-11 22:10:56

+0

elmarco,更新回答 – 2008-12-12 16:33:21

0

因此脚本加载脚本。运行一个killall(或任何在您的操作系统上可用的)命令,脚本完成后立即执行。

1

另一个选择是让脚本将自己设置为进程组组长,并在退出时在进程组中捕获killpg。

116

这对我的作品(改善感谢评论者):

trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT 
  • kill -- -$$发送SIGTERM整个进程组,因此也杀后裔。

  • 指定信号EXIT在使用set -e(更多详细信息here)时很有用。

17

陷阱“杀死$(工种-p)” EXIT

我会做给约翰的回答和使用工作只有轻微的变化-PR给杀限制正在运行的进程,并添加几个信号到列表:

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

工作-p并不适用于所有壳工作,如果调用一个子shell,可能除非它的输出重定向到一个文件,但不是一个烟斗。 (我假设它最初仅用于交互使用。)

什么如下:

trap 'while kill %% 2>/dev/null; do jobs > /dev/null; done' INT TERM EXIT [...] 

调用到“工作”需要符合Debian的仪表板外壳,从而未能更新当前的工作( “%%”)如果它丢失。

4

为了安全起见,我觉得它更好地定义清理功能和陷阱调用它:

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是空的 - 我结束了在“陷阱”(双关语意)。

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

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

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

为什么kill 0?因为嵌套的子壳也需要被杀死。这将取下the whole process tree

9

@tokland's answer中描述的trap 'kill 0' SIGINT SIGTERM EXIT解决方案非常好,但使用它时最新的Bash crashes with a segmantation fault。这是因为击,从V 4.3开始,允许陷阱递归,成为在此情况下无限:

  1. 壳过程临危SIGINTSIGTERMEXIT;
  2. 信号被捕获,执行kill 0,发送SIGTERM到组中的所有进程,包括shell本身;
  3. 去1 :)

这可以通过周围进行加工手动取消注册陷阱:

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

更花哨的方式,允许打印收到的信号和避免“终止” 消息:

#!/usr/bin/env bash 

trap_with_arg() { # from https://stackoverflow.com/a/2183063/804678 
    local func="$1"; shift 
    for sig in "[email protected]"; 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函数以避免不必要的信号,并隐藏输出中的“Terminated:”消息。谢谢Trevor Boyd Smith的建议!

0

我做了@ tokland的答案与知识相结合的适应从http://veithen.github.io/2014/11/16/sigterm-propagation.html时,我注意到,如果我运行一个前台进程(不与&转到后台)trap不会触发:

#!/bin/bash 

# killable-shell.sh: Kills itself and all children (the whole process group) when killed. 
# Adapted from http://stackoverflow.com/a/2173421 and http://veithen.github.io/2014/11/16/sigterm-propagation.html 
# Note: Does not work (and cannot work) when the shell itself is killed with SIGKILL, for then the trap is not triggered. 
trap "trap - SIGTERM && echo 'Caught SIGTERM, sending SIGTERM to process group' && kill -- -$$" SIGINT SIGTERM EXIT 

echo [email protected] 
"[email protected]" & 
PID=$! 
wait $PID 
trap - SIGINT SIGTERM EXIT 
wait $PID 

例它的工作:

$ bash killable-shell.sh sleep 100 
sleep 100 
^Z 
[1] + 31568 suspended bash killable-shell.sh sleep 100 

$ ps aux | grep "sleep" 
niklas 31568 0.0 0.0 19640 1440 pts/18 T 01:30 0:00 bash killable-shell.sh sleep 100 
niklas 31569 0.0 0.0 14404 616 pts/18 T 01:30 0:00 sleep 100 
niklas 31605 0.0 0.0 18956 936 pts/18 S+ 01:30 0:00 grep --color=auto sleep 

$ bg 
[1] + 31568 continued bash killable-shell.sh sleep 100 

$ kill 31568 
Caught SIGTERM, sending SIGTERM to process group 
[1] + 31568 terminated bash killable-shell.sh sleep 100 

$ ps aux | grep "sleep" 
niklas 31717 0.0 0.0 18956 936 pts/18 S+ 01:31 0:00 grep --color=auto sleep 
0

一个很好的版本,在Linux,BSD和MacOS X的第一部作品试图发送SIGTERM,如果没有成功,10秒后终止进程。

KillJobs() { 
    for job in $(jobs -p); do 
      kill -s SIGTERM $job > /dev/null 2>&1 || (sleep 10 && kill -9 $job > /dev/null 2>&1 &) 

    done 
} 

TrapQuit() { 
    # Whatever you need to clean here 
    KillJobs 
} 

trap TrapQuit EXIT 

请注意,作业不包括大孩子的过程。

相关问题