2008-12-11 90 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 

請注意,作業不包括大孩子的過程。

相關問題