我想杀死整个进程树。使用通用脚本语言实现这一目标的最佳方法是什么?我在寻找一个简单的解决办法。
当前回答
我不能评论(没有足够的声誉),所以我被迫添加一个新的答案,即使这不是一个真正的答案。
@olibre在2月28日给出了一个非常好的、彻底的回答,但有一个小问题。ps opgid= $PID的输出将包含小于5位的PID的前导空格,因为ps正在对列进行对齐(将数字右对齐)。在整个命令行中,这会导致一个负号,后面跟着空格(s),然后是组PID。简单的解决方案是通过管道将ps传送到tr以删除空格:
kill -- -$( ps opgid= $PID | tr -d ' ' )
其他回答
在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函数与许多其他答案类似,但它在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
}
现在是一些聪明的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美元),所以学习如何使用它,或者不要使用它。
如果你想按名称杀死一个进程:
killall -9 -g someprocessname
or
pgrep someprocessname | xargs pkill -9 -g
根据志刚的回答,这可以避免自我毁灭:
init_killtree() {
local pid=$1 child
for child in $(pgrep -P $pid); do
init_killtree $child
done
[ $pid -ne $$ ] && kill -kill $pid
}
推荐文章
- 如何通过脚本创建crontab
- 理解PrimeFaces处理/更新和JSF f:ajax执行/呈现属性
- 我可以在/etc/hosts中映射主机名*和端口*吗?
- Tar归档,从文件列表中获取输入
- 寻找不属于特定用户的文件
- 如何使用gnu cp命令将一个文件复制到多个目录
- 守护进程的错误响应:已被集装箱使用"
- 使用gcc命令行从.c文件构建.so文件
- bash中的字符串比较。[[:没有找到
- 如何在Bash脚本中使用密码执行sftp命令?
- 在Bash中将命令行参数转换为数组
- 为什么用反斜杠开始shell命令?
- 如何找到Java堆大小和内存使用(Linux)?
- 遍历带空格的文件列表
- 在Bash中检查传递的参数是否为文件或目录