bash 关闭钩子;或者,当主进程被杀死时,杀死所有后台进程
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18279540/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
bash shutdown hook; or, kill all background processes when main process is killed
提问by fabien
I have a bash that runs endless commands as background processes:
我有一个 bash,它可以作为后台进程运行无穷无尽的命令:
#!/bin/bash
function xyz() {
# some awk command
}
endlesscommand "param 1" | xyz & # async
pids=$!
endlesscommand "param 2" | xyz & # async
pids="$pids "$!
endlesscommand "param 3" | xyz # sync so the script doesn't leave
The only way to stop this script is (must be) Ctrl-C or kill and when that happens, I need to kill all the background processes listed in the $pids variable.
停止此脚本的唯一方法是(必须)Ctrl-C 或 kill,当发生这种情况时,我需要终止 $pids 变量中列出的所有后台进程。
How do I do that?
我怎么做?
If it was possible to catch the kill signal on the main process and execute a function when that happens (shutdown hook), I would do something like:
如果可以在主进程上捕获终止信号并在发生这种情况时执行函数(关闭挂钩),我会执行以下操作:
for $pid in $pids; do kill $pid; done;
But I can't find how to do this...
但我找不到如何做到这一点...
回答by phs
Here's a trap that doesn't need you to track pids:
这是一个不需要您跟踪的陷阱pids:
trap 'jobs -p | xargs kill' EXIT
EDIT: @Barmar asked if this works within non-sourced scripts, where job control isn't usually available. It does. Consider this script:
编辑:@Barmar 询问这是否适用于非源脚本,其中作业控制通常不可用。确实如此。考虑这个脚本:
$ cat no-job-control
#! /bin/bash
set -e -o pipefail
# Prove job control is off
if suspend
then
echo suspended
else
echo suspension failed, job control must be off
fi
echo
# Set up the trap
trap 'jobs -p | xargs kill' EXIT
# Make some work
(echo '=> Starting 0'; sleep 5; echo '=> Finishing 0') &
(echo '=> Starting 1'; sleep 5; echo '=> Finishing 1') &
(echo '=> Starting 2'; sleep 5; echo '=> Finishing 2') &
echo "What's in jobs -p?"
echo
jobs -p
echo
echo "Ok, exiting now"
echo
When run we see the pids of the three group leaders, and then see them killed:
运行时我们看到三个组长的pid,然后看到他们被杀:
$ ./no-job-control
./no-job-control: line 6: suspend: cannot suspend: no job control
suspension failed, job control must be off
=> Starting 0
What's in jobs -p?
=> Starting 1
54098
54099
54100
Ok, exiting now
=> Starting 2
./no-job-control: line 31: 54098 Terminated: 15 ( echo '=> Starting 0'; sleep 5; echo '=> Finishing 0' )
./no-job-control: line 31: 54099 Terminated: 15 ( echo '=> Starting 1'; sleep 5; echo '=> Finishing 1' )
./no-job-control: line 31: 54100 Terminated: 15 ( echo '=> Starting 2'; sleep 5; echo '=> Finishing 2' )
If we instead comment out the trapline and re-run, the three jobs do not die and in fact print out their final messages a few seconds later. Notice the returned prompt interleaved with the final outputs.
如果我们改为注释掉该trap行并重新运行,这三个作业不会终止,实际上几秒钟后会打印出它们的最终消息。注意返回的提示与最终输出交错。
$ ./no-job-control
./no-job-control: line 6: suspend: cannot suspend: no job control
suspension failed, job control must be off
=> Starting 0
What's in jobs -p?
54110
54111
54112
=> Starting 1
Ok, exiting now
=> Starting 2
$ => Finishing 0
=> Finishing 2
=> Finishing 1
回答by konsolebox
You can make use of pgrep and a function to kill all processes created under the main process like this. This would not only kill the direct child processes but also those created under it.
您可以使用 pgrep 和一个函数来终止在主进程下创建的所有进程,如下所示。这不仅会杀死直接子进程,还会杀死在其下创建的子进程。
#!/bin/bash
function killchildren {
local LIST=() IFS=$'\n' A
read -a LIST -d '' < <(exec pgrep -P "")
local A SIGNAL="${2:-SIGTERM}"
for A in "${LIST[@]}"; do
killchildren_ "$A" "$SIGNAL"
done
}
function killchildren_ {
local LIST=()
read -a LIST -d '' < <(exec pgrep -P "")
kill -s "" ""
if [[ ${#LIST[@]} -gt 0 ]]; then
local A
for A in "${LIST[@]}"; do
killchildren_ "$A" ""
done
fi
}
trap 'killchildren "$BASHPID"' EXIT
endlesscommand "param 1" &
endlesscommand "param 2" &
endlesscommand "param 3" &
while pgrep -P "$BASHPID" >/dev/null; do
wait
done
As for your original code, it would be better to just use arrays, and you also don't need to use a for loop:
至于你的原始代码,最好只使用数组,而且你也不需要使用 for 循环:
#!/bin/bash
trap 'kill "${pids[@]}"' EXIT
pids=()
endlesscommand "param 1" & # async
pids+=("$!")
endlesscommand "param 2" & # async
pids+=("$!")
endlesscommand "param 3" & # syncing this is not a good idea since if the main process would end along with it if it ends earlier.
pids+=("$!")
while pgrep -P "$BASHPID" >/dev/null; do
wait
done
Original function reference: http://www.linuxquestions.org/questions/blog/konsolebox-210384/bash-functions-to-list-and-kill-or-send-signals-to-process-trees-34624/
原函数参考:http: //www.linuxquestions.org/questions/blog/konsolebox-210384/bash-functions-to-list-and-kill-or-send-signals-to-process-trees-34624/
回答by iamauser
kill `ps axl | grep "endlesscommand" | awk '{printf " "}'`
This will look for the parent processes that is affecting "endlesscommand"
这将寻找影响“endlesscommand”的父进程

