我想杀死整个进程树。使用通用脚本语言实现这一目标的最佳方法是什么?我在寻找一个简单的解决办法。
当前回答
现在是一些聪明的shell编程。
这种解决方案是有代价的,但至少它是基于日常迭代和递归的。通过仔细注意排版命令并在适当的地方将它们转换为声明或本地,可以将其转换为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(在awk中为4美元),所以学习如何使用它,或者不要使用它。
其他回答
如果你的系统上有pstrree和perl,你可以尝试这样做:
perl -e 'kill 9, (`pstree -p PID` =~ m/\((\d+)\)/sg)'
立即终止整个进程组,就像^C一样:
PID="$(pgrep -f unique_command_line_part)"
if [[ -n "$PID" ]]
then
PGID="$(ps --no-headers -p $PID -o pgid)"
kill -SIGINT -- -${PGID// /}
fi
每一行都在这个答案中进行了解释
要递归地杀死进程树,请使用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 $@
输入ps -ef检查进程id。 通过输入Kill -9 <pid>来终止进程
修改后的志刚回答:
#!/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