我想杀死整个进程树。使用通用脚本语言实现这一目标的最佳方法是什么?我在寻找一个简单的解决办法。
当前回答
要递归地杀死进程树,请使用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 $@
其他回答
如果你的系统上有pstrree和perl,你可以尝试这样做:
perl -e 'kill 9, (`pstree -p PID` =~ m/\((\d+)\)/sg)'
在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)来玩游戏,看看另一种行为。在这种情况下,child将在达到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" '
Above script will create 4 new child processes. We are storing pids of all child process and looping over them to check if they are finished their execution or still running. Child process will execution till CMD_TIME time. But if CNT_TIME_OUT timeout reach , All children will get killed by parent process. You can switch timing and play around with script to see behavior. One drawback of this approach is , it is using group id for killing all child tree. But parent process itself belong to same group so it will also get killed.
如果不希望终止父进程,可能需要为父进程分配其他组id。
更多细节可以在这里找到,
在shell脚本中杀死子进程
使用进程组ID (PGID)杀死属于同一进程树的所有进程
使用默认信号(TERM = 15) 使用信号kill (9)
您可以从同一进程树的任何进程id (PID)中检索PGID
杀- - - $ (ps - o pgid = $ PID | grep - o '[0 - 9] * ')(信号) $(ps -o pgid= $PID | grep -o '[0-9]*')(信号kill)
特别感谢tanager和Speakus对$PID剩余空间和OSX兼容性的贡献。
解释
kill -9 -"$PGID" => Send signal 9 (KILL) to all child and grandchild... PGID=$(ps opgid= "$PID") => Retrieve the Process-Group-ID from any Process-ID of the tree, not only the Process-Parent-ID. A variation of ps opgid= $PID is ps -o pgid --no-headers $PID where pgid can be replaced by pgrp. But: ps inserts leading spaces when PID is less than five digits and right aligned as noticed by tanager. You can use: PGID=$(ps opgid= "$PID" | tr -d ' ') ps from OSX always print the header, therefore Speakus proposes: PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )" grep -o [0-9]* prints successive digits only (does not print spaces or alphabetical headers).
更多命令行
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在终止整个树的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)。 这就是为什么我一开始觉得杀了。-$PID就够了。但如果进程是在Makefile中生成的,则进程ID与组ID不同。
我认为kill——-$(ps -o pgid= $PID | grep -o[0-9]*)是当从不同的Group ID(另一个进程树)调用时杀死整个进程树的最好的简单技巧。
这是我使用bash脚本杀死所有子进程的版本。 它不使用递归,依赖于pgrep命令。
Use
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//,/ }
我知道这很老了,但这是我找到的更好的解决方案:
killtree() {
for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do
echo Terminating: $p
kill $p
done
}
推荐文章
- Linux命令将域名转换为IP
- 如何从命令行在windows中找到mysql数据目录
- 如何在Makefile中设置子进程的环境变量
- 如何让“wc -l”打印没有文件名的行数?
- 有效地测试Linux上的端口是否打开?
- 如何从另一个文件A中删除文件B中出现的行?
- 对以制表符分隔的文件进行排序
- Shell脚本删除超过n天的目录
- 使用sudo时未找到命令
- 当有命令行参数时,如何使用GDB分析程序的核心转储文件?
- 如何检查shell脚本中是否存在命令?
- 如何使用查找命令从列表中查找所有具有扩展名的文件?
- 如何强制从另一个SSH会话分离屏幕?
- 如何打破一个循环在Bash?
- 如何将文件指针(file * fp)转换为文件描述符(int fd)?