我想杀死整个进程树。使用任何常见的脚本语言来做到这一点的最佳方法是什么?我正在寻找一个简单的解决方案。
34 回答
您没有说要杀死的树是否是单个进程组。(如果树是从服务器启动或 shell 命令行分叉的结果,通常会出现这种情况。)您可以使用 GNU ps 发现进程组,如下所示:
ps x -o "%p %r %y %x %c "
如果它是您要杀死的进程组,只需使用kill(1)
命令,但不要给它一个进程号,而是给它组号的否定。例如要杀死组 5112 中的每个进程,请使用kill -TERM -- -5112
.
使用进程组 ID ( )杀死属于同一进程树的所有进程PGID
kill -- -$PGID
使用默认信号 (TERM
= 15)kill -9 -$PGID
使用信号KILL
(9)
您可以从同一进程树PGID
的任何Process-ID ( ) 中检索PID
kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*')
(信号TERM
)kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*')
(信号KILL
)
特别感谢tanager和Speakus对$PID
剩余空间和 OSX 兼容性的贡献。
解释
kill -9 -"$PGID"
=>KILL
向所有子孙发送信号 9 ( )...PGID=$(ps opgid= "$PID")
=>从树的任何Process-ID中检索Process-Group-ID,而不仅仅是Process-Parent-ID。的变体是where可以替换为。但:ps opgid= $PID
ps -o pgid --no-headers $PID
pgid
pgrp
grep -o [0-9]*
仅打印连续数字(不打印空格或字母标题)。
更多命令行
PGID=$(ps -o pgid= $PID | grep -o [0-9]*)
kill -TERM -"$PGID" # kill -15
kill -INT -"$PGID" # correspond to [CRTL+C] from keyboard
kill -QUIT -"$PGID" # correspond to [CRTL+\] from keyboard
kill -CONT -"$PGID" # restart a stopped process (above signals do not kill it)
sleep 2 # wait terminate process (more time if required)
kill -KILL -"$PGID" # kill -9 if it does not intercept signals (or buggy)
局限性
- 正如davide和Hubert Kario所注意到的,当
kill
被属于同一棵树的进程调用时,有kill
可能在终止整个树的杀戮之前杀死自己。 - 因此,请务必使用具有不同Process-Group-ID的进程运行命令。
很长的故事
> cat run-many-processes.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./child.sh background &
./child.sh foreground
echo "ProcessID=$$ ends ($0)"
> cat child.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./grandchild.sh background &
./grandchild.sh foreground
echo "ProcessID=$$ ends ($0)"
> cat grandchild.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
sleep 9999
echo "ProcessID=$$ ends ($0)"
使用“&”在后台运行进程树
> ./run-many-processes.sh &
ProcessID=28957 begins (./run-many-processes.sh)
ProcessID=28959 begins (./child.sh)
ProcessID=28958 begins (./child.sh)
ProcessID=28960 begins (./grandchild.sh)
ProcessID=28961 begins (./grandchild.sh)
ProcessID=28962 begins (./grandchild.sh)
ProcessID=28963 begins (./grandchild.sh)
> PID=$! # get the Parent Process ID
> PGID=$(ps opgid= "$PID") # get the Process Group ID
> ps fj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
28348 28349 28349 28349 pts/3 28969 Ss 33021 0:00 -bash
28349 28957 28957 28349 pts/3 28969 S 33021 0:00 \_ /bin/sh ./run-many-processes.sh
28957 28958 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./child.sh background
28958 28961 28957 28349 pts/3 28969 S 33021 0:00 | | \_ /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3 28969 S 33021 0:00 | | | \_ sleep 9999
28958 28963 28957 28349 pts/3 28969 S 33021 0:00 | | \_ /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3 28969 S 33021 0:00 | | \_ sleep 9999
28957 28959 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./child.sh foreground
28959 28960 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3 28969 S 33021 0:00 | | \_ sleep 9999
28959 28962 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3 28969 S 33021 0:00 | \_ sleep 9999
28349 28969 28969 28349 pts/3 28969 R+ 33021 0:00 \_ ps fj
该命令pkill -P $PID
不会杀死孙子:
> pkill -P "$PID"
./run-many-processes.sh: line 4: 28958 Terminated ./child.sh background
./run-many-processes.sh: line 4: 28959 Terminated ./child.sh foreground
ProcessID=28957 ends (./run-many-processes.sh)
[1]+ Done ./run-many-processes.sh
> ps fj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
28348 28349 28349 28349 pts/3 28987 Ss 33021 0:00 -bash
28349 28987 28987 28349 pts/3 28987 R+ 33021 0:00 \_ ps fj
1 28963 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
1 28962 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
1 28961 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
1 28960 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
该命令kill -- -$PGID
会杀死包括孙子进程在内的所有进程。
> kill -- -"$PGID" # default signal is TERM (kill -15)
> kill -CONT -"$PGID" # awake stopped processes
> kill -KILL -"$PGID" # kill -9 to be sure
> ps fj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
28348 28349 28349 28349 pts/3 29039 Ss 33021 0:00 -bash
28349 29039 29039 28349 pts/3 29039 R+ 33021 0:00 \_ ps fj
结论
我在这个例子中注意到PID
并且PGID
是相等的 ( 28957
)。
这就是为什么我最初认为kill -- -$PID
就足够了。但是如果进程是在 Process ID 与 Group ID 不同的Makefile
情况下产生的。
我认为当从不同的组 ID(另一个进程树)kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)
调用时,杀死整个进程树是最好的简单技巧。
pkill -TERM -P 27888
这将杀死所有父进程 ID 为 27888 的进程。
或者更健壮:
CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS
哪个计划在 33 秒后杀死并礼貌地要求进程终止。
请参阅此答案以终止所有后代。
要递归地终止进程树,请使用 killtree():
#!/bin/bash
killtree() {
local _pid=$1
local _sig=${2:--TERM}
kill -stop ${_pid} # needed to stop quickly forking parent from producing children between child killing and parent killing
for _child in $(ps -o pid --no-headers --ppid ${_pid}); do
killtree ${_child} ${_sig}
done
kill -${_sig} ${_pid}
}
if [ $# -eq 0 -o $# -gt 2 ]; then
echo "Usage: $(basename $0) <pid> [signal]"
exit 1
fi
killtree $@
来自pslist包的rkill命令将给定的信号(或SIGTERM
默认情况下)发送到指定的进程及其所有后代:
rkill [-SIG] pid/name...
我使用了这里描述的方法的一点修改版本: https ://stackoverflow.com/a/5311362/563175
所以它看起来像这样:
kill `pstree -p 24901 | sed 's/(/\n(/g' | grep '(' | sed 's/(\(.*\)).*/\1/' | tr "\n" " "`
其中 24901 是父母的 PID。
它看起来很丑,但它的工作很完美。
布拉德的回答也是我推荐的,除了awk
如果你使用.--ppid
ps
for child in $(ps -o pid -ax --ppid $PPID) do ....... done
zhigang回答的修改版:
#!/usr/bin/env bash
set -eu
killtree() {
local pid
for pid; do
kill -stop $pid
local cpid
for cpid in $(pgrep -P $pid); do
killtree $cpid
done
kill $pid
kill -cont $pid
wait $pid 2>/dev/null || true
done
}
cpids() {
local pid=$1 options=${2:-} space=${3:-}
local cpid
for cpid in $(pgrep -P $pid); do
echo "$space$cpid"
if [[ "${options/a/}" != "$options" ]]; then
cpids $cpid "$options" "$space "
fi
done
}
while true; do sleep 1; done &
cpid=$!
for i in $(seq 1 2); do
cpids $$ a
sleep 1
done
killtree $cpid
echo ---
cpids $$ a
我无法发表评论(没有足够的声誉),所以我不得不添加一个新的答案,即使这不是一个真正的答案。
@olibre 在 2 月 28 日给出的其他非常好的和彻底的答案存在一个小问题。 的输出ps opgid= $PID
将包含小于五位数字的 PID 的前导空格,因为ps
正在证明列的正确性(右对齐数字)。在整个命令行中,这会产生一个负号,后跟空格,然后是组 PID。简单的解决方案是通过管道ps
删除tr
空格:
kill -- -$( ps opgid= $PID | tr -d ' ' )
如果您知道传递父进程的 pid,那么这里有一个应该可以工作的 shell 脚本:
for child in $(ps -o pid,ppid -ax | \
awk "{ if ( \$2 == $pid ) { print \$1 }}")
do
echo "Killing child process $child because ppid = $pid"
kill $child
done
要添加到 Norman Ramsey 的答案,如果您想创建一个进程组,可能值得看一下 setid 。
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html
如果调用进程不是进程组领导,则 setsid() 函数将创建一个新会话。返回时,调用进程将是这个新会话的会话负责人,应是新进程组的进程组负责人,并且不应有控制终端。调用进程的进程组 ID 应设置为等于调用进程的进程 ID。调用进程应是新进程组中的唯一进程,也是新会话中的唯一进程。
我的意思是你可以从开始的过程中创建一个组。我在 php 中使用了它,以便在启动它后能够杀死整个进程树。
这可能是个坏主意。我会对评论感兴趣。
使用 python 使用psutil非常容易做到这一点。只需使用 pip 安装 psutil,然后您就拥有一整套流程操作工具:
def killChildren(pid):
parent = psutil.Process(pid)
for child in parent.get_children(True):
if child.is_running():
child.terminate()
根据志刚的回答,这样可以避免自残:
init_killtree() {
local pid=$1 child
for child in $(pgrep -P $pid); do
init_killtree $child
done
[ $pid -ne $$ ] && kill -kill $pid
}
以下 shell 函数类似于许多其他答案,但它适用于 Linux 和 BSD(OS X 等),没有外部依赖项,例如pgrep
:
killtree() {
local parent=$1 child
for child in $(ps -o ppid= -o pid= | awk "\$1==$parent {print \$2}"); do
killtree $child
done
kill $parent
}
如果要按名称杀死进程:
killall -9 -g someprocessname
或者
pgrep someprocessname | xargs pkill -9 -g
这是我使用 bash 脚本杀死所有子进程的版本。它不使用递归并且依赖于 pgrep 命令。
采用
killtree.sh PID SIGNAL
killtrees.sh 的内容
#!/bin/bash
PID=$1
if [ -z $PID ];
then
echo "No pid specified"
fi
PPLIST=$PID
CHILD_LIST=`pgrep -P $PPLIST -d,`
while [ ! -z "$CHILD_LIST" ]
do
PPLIST="$PPLIST,$CHILD_LIST"
CHILD_LIST=`pgrep -P $CHILD_LIST -d,`
done
SIGNAL=$2
if [ -z $SIGNAL ]
then
SIGNAL="TERM"
fi
#do substring from comma to space
kill -$SIGNAL ${PPLIST//,/ }
这是@zhigang 的答案的变体,它没有 AWK,仅依赖 Bash 的本机解析可能性:
function killtree {
kill -STOP "$1"
ps -e -o pid= -o ppid= | while read -r pid ppid
do
[[ $ppid = $1 ]] || continue
killtree "$pid" || true # Skip over failures
done
kill -CONT "$1"
kill -TERM "$1"
}
它似乎在 Mac 和 Linux 上都能正常工作。在您不能依赖管理流程组的情况下——例如在编写脚本来测试必须在多个环境中构建的软件时——这种树遍历技术绝对有用。
谢谢你的智慧,伙计们。我的脚本在退出时留下了一些子进程,否定提示使事情变得更容易。如有必要,我编写了此函数以在其他脚本中使用:
# kill my group's subprocesses: killGroup
# kill also myself: killGroup -x
# kill another group's subprocesses: killGroup N
# kill that group all: killGroup -x N
# N: PID of the main process (= process group ID).
function killGroup () {
local prid mainpid
case $1 in
-x) [ -n "$2" ] && kill -9 -$2 || kill -9 -$$ ;;
"") mainpid=$$ ;;
*) mainpid=$1 ;;
esac
prid=$(ps ax -o pid,pgid | grep $mainpid)
prid=${prid//$mainpid/}
kill -9 $prid 2>/dev/null
return
}
干杯。
如果你的系统上有 pstree 和 perl,你可以试试这个:
perl -e 'kill 9, (`pstree -p PID` =~ m/\((\d+)\)/sg)'
在孩子之前杀死父母可能会更好;否则父母可能会在他自己被杀之前再次产生新的孩子。这些将在杀戮中幸存下来。
我的 ps 版本与上面的不同;也许太老了,因此奇怪的grepping...
使用 shell 脚本而不是 shell 函数有很多优点......
不过,基本上是志刚的想法
#!/bin/bash
if test $# -lt 1 ; then
echo >&2 "usage: kiltree pid (sig)"
fi ;
_pid=$1
_sig=${2:-TERM}
_children=$(ps j | grep "^[ ]*${_pid} " | cut -c 7-11) ;
echo >&2 kill -${_sig} ${_pid}
kill -${_sig} ${_pid}
for _child in ${_children}; do
killtree ${_child} ${_sig}
done
以下已在 FreeBSD、Linux 和 MacOS X 上测试过,仅依赖于 pgrep 和 kill(ps -o 版本在 BSD 下不起作用)。第一个参数是必须终止其子代的父 pid。第二个参数是一个布尔值,用于确定父 pid 是否也必须终止。
KillChilds() {
local pid="${1}"
local self="${2:-false}"
if children="$(pgrep -P "$pid")"; then
for child in $children; do
KillChilds "$child" true
done
fi
if [ "$self" == true ]; then
kill -s SIGTERM "$pid" || (sleep 10 && kill -9 "$pid" &)
fi
}
KillChilds $$ > /dev/null 2>&1
这会将 SIGTERM 发送到 shell 脚本中的任何子/孙进程,如果 SIGTERM 不成功,它将等待 10 秒然后发送 kill。
之前的回答:
以下方法也有效,但会在 BSD 上杀死 shell 本身。
KillSubTree() {
local parent="${1}"
for child in $(ps -o pid=$parent); do
if [ $$ -ne $child ]; then (kill -s SIGTERM $child || (sleep 10 && kill -9 $child & )) > /dev/null 2>&1 ; fi
done
}
# Example lanch from within script
KillSubTree $$ > /dev/null 2>&1
我进一步开发了zhigang、xyuri和solidsneck的解决方案:
#!/bin/bash
if test $# -lt 1 ; then
echo >&2 "usage: kiltree pid (sig)"
exit 1 ;
fi ;
_pid=$1
_sig=${2:-TERM}
# echo >&2 "killtree($_pid) mypid = $$"
# ps axwwf | grep -6 "^[ ]*$_pid " >&2 ;
function _killtree () {
local _children
local _child
local _success
if test $1 -eq $2 ; then # this is killtree - don't commit suicide!
echo >&2 "killtree can´t kill it´s own branch - some processes will survive." ;
return 1 ;
fi ;
# this avoids that children are spawned or disappear.
kill -SIGSTOP $2 ;
_children=$(ps -o pid --no-headers --ppid $2) ;
_success=0
for _child in ${_children}; do
_killtree $1 ${_child} $3 ;
_success=$(($_success+$?)) ;
done ;
if test $_success -eq 0 ; then
kill -$3 $2
fi ;
# when a stopped process is killed, it will linger in the system until it is continued
kill -SIGCONT $2
test $_success -eq 0 ;
return $?
}
_killtree $$ $_pid $_sig
这个版本将避免杀死它的祖先——这会在以前的解决方案中导致大量的子进程。
在确定子列表之前正确停止进程,这样就不会创建或消失新的子进程。
被杀死后,停止的作业必须继续从系统中消失。
老问题,我知道,但所有的回复似乎都在叫 ps,我不喜欢。
这种基于 awk 的解决方案不需要递归,只调用 ps 一次。
awk 'BEGIN {
p=1390
while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2
o=1
while (o==1) {
o=0
split(p, q, " ")
for (i in q) if (a[q[i]]!="") {
p=p""a[q[i]]
o=1
a[q[i]]=""
}
}
system("kill -TERM "p)
}'
或单行:
awk 'BEGIN {p=1390;while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2;o=1;while (o==1) {o=0;split(p, q, " ");for (i in q) {if (a[q[i]]!="") {p=p""a[q[i]];o=1;a[q[i]]=""}}}system("kill -TERM "p)}'
基本上我们的想法是我们建立一个 parent:child 条目的数组 (a),然后在数组中循环查找匹配的父母的孩子,然后将它们添加到我们的父母列表 (p) 中。
如果你不想杀死顶级进程,那么做
sub(/[0-9]*/, "", p)
就在 system() 行将其从杀伤集中删除之前。
请记住,这里存在竞争条件,但所有解决方案都是如此(据我所知)。它可以满足我的需要,因为我需要它的脚本不会产生很多短命的孩子。
给读者的一个练习是让它成为一个 2-pass 循环:在第一次通过后,向 p 列表中的所有进程发送 SIGSTOP,然后循环以再次运行 ps,在第二次通过后发送 SIGTERM,然后发送 SIGCONT。如果你不关心美好的结局,那么我想第二遍可能只是 SIGKILL。
ps -o pid= --ppid $PPID | xargs kill -9
如果您知道要杀死的东西的 pid,通常可以从会话 id 以及同一会话中的所有内容中获取。我会仔细检查,但我将它用于在我想死的循环中启动 rsync 的脚本,而不是像我只是 killall'd rsync 那样启动另一个(因为循环)。
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid 21709))
如果你不知道 pid 你仍然可以嵌套更多
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid $(pgrep rsync )))
在 sh 中,jobs 命令将列出后台进程。在某些情况下,最好先杀死最新的进程,例如旧的进程创建了一个共享套接字。在这些情况下,以相反的顺序对 PID 进行排序。有时您想等待作业在它们停止之前在磁盘上写一些东西或类似的东西。
如果没有必要,不要杀人!
for SIGNAL in TERM KILL; do
for CHILD in $(jobs -s|sort -r); do
kill -s $SIGNAL $CHILD
sleep $MOMENT
done
done
在 shell 脚本中杀死子进程:
很多时候我们需要杀死由于某种原因被挂起或阻塞的子进程。例如。FTP 连接问题。
有两种方法,
1) 为每个子进程创建单独的新父进程,一旦超时,它将监视并终止子进程。
如下创建 test.sh,
#!/bin/bash
declare -a CMDs=("AAA" "BBB" "CCC" "DDD")
for CMD in ${CMDs[*]}; do
(sleep 10 & PID=$!; echo "Started $CMD => $PID"; sleep 5; echo "Killing $CMD => $PID"; kill $PID; echo "$CMD Completed.") &
done
exit;
并使用以下命令在其他终端中查看名称为“test”的进程。
watch -n1 'ps x -o "%p %r %c" | grep "test" '
上面的脚本将创建 4 个新的子进程及其父进程。每个子进程将运行 10 秒。但是一旦达到 5 秒的超时时间,它们各自的父进程就会杀死这些子进程。所以孩子将无法完成执行(10秒)。玩转这些时间(开关 10 和 5)以查看另一种行为。在这种情况下,孩子将在 10 秒超时之前在 5 秒内完成执行。
2)一旦超时,让当前父进程监控并杀死子进程。这不会创建单独的父级来监视每个子级。您还可以在同一父级中正确管理所有子进程。
如下创建 test.sh,
#!/bin/bash
declare -A CPIDs;
declare -a CMDs=("AAA" "BBB" "CCC" "DDD")
CMD_TIME=15;
for CMD in ${CMDs[*]}; do
(echo "Started..$CMD"; sleep $CMD_TIME; echo "$CMD Done";) &
CPIDs[$!]="$RN";
sleep 1;
done
GPID=$(ps -o pgid= $$);
CNT_TIME_OUT=10;
CNT=0;
while (true); do
declare -A TMP_CPIDs;
for PID in "${!CPIDs[@]}"; do
echo "Checking "${CPIDs[$PID]}"=>"$PID;
if ps -p $PID > /dev/null ; then
echo "-->"${CPIDs[$PID]}"=>"$PID" is running..";
TMP_CPIDs[$PID]=${CPIDs[$PID]};
else
echo "-->"${CPIDs[$PID]}"=>"$PID" is completed.";
fi
done
if [ ${#TMP_CPIDs[@]} == 0 ]; then
echo "All commands completed.";
break;
else
unset CPIDs;
declare -A CPIDs;
for PID in "${!TMP_CPIDs[@]}"; do
CPIDs[$PID]=${TMP_CPIDs[$PID]};
done
unset TMP_CPIDs;
if [ $CNT -gt $CNT_TIME_OUT ]; then
echo ${CPIDs[@]}"PIDs not reponding. Timeout reached $CNT sec. killing all childern with GPID $GPID..";
kill -- -$GPID;
fi
fi
CNT=$((CNT+1));
echo "waiting since $b secs..";
sleep 1;
done
exit;
并使用以下命令在其他终端中查看名称为“test”的进程。
watch -n1 'ps x -o "%p %r %c" | grep "test" '
上面的脚本将创建 4 个新的子进程。我们正在存储所有子进程的 pids 并循环它们以检查它们是否已完成执行或仍在运行。子进程将执行到 CMD_TIME 时间。但是如果 CNT_TIME_OUT 超时,所有子进程都会被父进程杀死。您可以切换时间并使用脚本来查看行为。这种方法的一个缺点是,它使用组 ID 来杀死所有子树。但是父进程本身属于同一个组,所以它也会被杀死。
如果您不希望父进程被杀死,您可能需要将其他组 ID 分配给父进程。
更多详情可在这找到,
该脚本也可以工作:
#/bin/sh
while true
do
echo "Enter parent process id [type quit for exit]"
read ppid
if [ $ppid -eq "quit" -o $ppid -eq "QUIT" ];then
exit 0
fi
for i in `ps -ef| awk '$3 == '$ppid' { print $2 }'`
do
echo killing $i
kill $i
done
done
我知道这很旧,但这是我找到的更好的解决方案:
killtree() {
for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do
echo Terminating: $p
kill $p
done
}
仅使用属于组的进程名称杀死组:
kill -- -$(ps -ae o pid,pgrp,cmd | grep "[e]xample.py" | awk '{print $2}' | tail -1)
这是对 olibre 答案的修改,但您不需要知道 PID,只需知道组成员的名称。
解释:
要获取组 ID,您可以使用所示的参数执行 ps 命令,将其 grep 用于您的命令,但使用引号格式化 example.py 并使用括号作为第一个字母(这会过滤掉 grep 命令本身)然后过滤它awk 获取第二个字段,即组 ID。尾部 -1 去掉了重复的组 ID。您使用 $() 语法将所有这些放在一个变量中,瞧——您将获得组 ID。因此,您将 $(mess) 替换为上面的 -groupid。
现在进行一些巧妙的 shell 编程。
这种解决方案是有代价的,但至少它是基于日常迭代和递归的。通过仔细注意typeset
命令并将它们转换为declare
或local
在适当的情况下,可以将其转换为 bash。
讨论
在杀死一个进程时,必须处理这样一个事实:它可能是许多孩子的父母,并且每个孩子都可能是更多孩子的父母,等等。
该怎么办?
如果只有一个函数可以测试一个进程是否有子进程,另一个函数可以返回父进程的子 PID。
然后,游戏会简单得多,因为您可以创建一个循环来迭代一个 PID 列表,在杀死它之前检查每个 PID。如果没有子进程,就终止进程。如果有孩子,则递归调用驱动函数并将获取父母孩子的 PID 的函数的结果传递给它。
基本情况操作(进程没有子进程)。
#!/bin/ksh
function killProcess ()
{
typeset -r PID=$1
if [[ ! isProcess $PID ]]
then
echo -e "Process $PID cannot be terminated because it does not exist.\n" 1>&2
return 1
elif [[ kill -s TERM $PID ]] && [[ ! isProcess $PID ]]
then
echo -e "Process $PID was terminated.\n" 1>&2
return 0
elif kill -s KILL $PID
echo -e "Process $PID killed with SIGKILL (9) signal. No time to clean up potential files.\n" 1>&2
return 0
elif isZombie $PID
then
echo -e "Process $PID in the zombie status.\n" 1>&2
return 2
else
echo -e "Process $PID is alive. SIGTERM and SIGKILL had no effect. It is not a zombie.\n" 1>&2
fi
return 3
}
function attemptToKillPid ()
{
typeset -r PID=$1
if killProcess $PID
then
return 0
fi
ppid=$(getParentPid $pid)
echo -e "Process $pid of parent $ppid was not able to be killed.\n" 1>&2
return 1
}
一般案例动作(进程有子进程)。
function killPidFamily ()
{
typeset -r PROCESSES=$*
typeset -ir NUM_PROCESSES_TO_KILL=$(countLines $PROCESSES)
typeset -i numKilledProcesses=0
typeset ppid
for pid in $PROCESSES
do
pid=$(trim $pid)
if ! hasChildPids $pid
then
attemptToKillPid $pid && (( numKilledProcesses++ ))
else
killPidFamily $(getChildPids $pid) && attemptToKillPid $pid && (( numKilledProcesses++ ))
fi
done
(( numKilledProcesses == NUM_PROCESSES_TO_KILL ))
return $?
}
支持功能库。
#!/bin/ksh
function trim ()
{
echo -n "$1" | tr -d [:space:]
}
function countLines ()
{
typeset -r $LIST=$*
trim $(echo $LIST | wc -l | awk {'print $1'})
}
function getProcesses ()
{
# NOTE: -o pgid below would be $4 in awk.
ps -e -o comm,pid,ppid,pgid,user,ruid,euid,group,rgid,egid,etime,etimes,stat --no-headers
}
function getProcess ()
{
typeset -r PID=$1
ps -p $PID -o comm,pid,ppid,pgid,user,ruid,euid,group,rgid,egid,etime,etimes,stat --no-headers
}
function isProcess ()
{
typeset -r PID=$1
ps -p $PID -o pid --no-headers 1>&2
return $?
}
function getProcessStatus ()
{
typeset -r PID=$1
trim $(ps -p $PID -o stat --no-headers)
}
function isZombie ()
{
typeset -r PID=$1
typeset processStatus
processStatus=$(getProcessStatus $PID)
[[ "$processStatus" == "Z" ]]
return $?
}
function hasChildPids ()
{
typeset -r PPID=$1
echo $(getProcesses) | awk '{print $3}' | sort -n | uniq | grep "^${PPID}$"
return $?
}
function getChildPids ()
{
typeset -r PPID=$1
echo $(getProcesses) | awk '{print $2, $3}' | sort -k 2 | awk "\$2 == $PPID {print \$1}" | sort -n
}
function getParentPid ()
{
typeset -r PID=$1
trim $(echo $(getProcess $PID) | awk '{print $3}')
}
通过这种方式,您可以确定进程树正在从分支中被破坏,向上移动到根。这有助于避免产生僵尸和其他不良情况的可能性。
现在,您已经看到了最昂贵的方法(一次杀死一个进程),请研究如何更改此解决方案以使用 PGID(进程组 ID)。该getProcesses ()
函数已经打印了 PGID ( $4
in awk
),所以学习如何使用它,或者不要。
一次杀死整个进程组,就像这样^C
做:
PID="$(pgrep -f unique_command_line_part)"
if [[ -n "$PID" ]]
then
PGID="$(ps --no-headers -p $PID -o pgid)"
kill -SIGINT -- -${PGID// /}
fi
此答案中解释了每一行
键入ps -ef
检查进程ID。通过键入终止进程kill -9 <pid>