Я хочу вбити ціле дерево процесу. Який найкращий спосіб зробити це за допомогою будь-яких загальних мов сценаріїв? Я шукаю просте рішення.
chronos
або herodes
.
Я хочу вбити ціле дерево процесу. Який найкращий спосіб зробити це за допомогою будь-яких загальних мов сценаріїв? Я шукаю просте рішення.
chronos
або herodes
.
Відповіді:
Ви не кажете, чи дерево, яке ви хочете вбити, - це одна група процесів. (Це часто трапляється, якщо дерево є результатом розгалуження із запуску сервера або командного рядка оболонки.) Ви можете виявити групи процесів за допомогою GNU ps таким чином:
ps x -o "%p %r %y %x %c "
Якщо це група процесів, яку ви хочете вбити, просто використовуйте kill(1)
команду, але замість того, щоб давати їй номер процесу, надайте їй заперечення номера групи. Наприклад, щоб вбити кожен процес у групі 5112, використовуйте kill -TERM -- -5112
.
pgrep
може запропонувати більш простий спосіб знайти ідентифікатор групи процесів. Наприклад, щоб вбити групу процесу my-script.sh, запустіть kill -TERM -$(pgrep -o my-script.sh)
.
ps -o pid --no-headers --ppid $PARENT_PID
ps x -o "%r %p %y %x %c" | sort -nk1,2
Вбийте всі процеси, що належать до одного дерева процесів, використовуючи ідентифікатор групи процесів ( 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"
=> Надіслати сигнал 9 ( KILL
) всім дітям та онукам ...PGID=$(ps opgid= "$PID")
=> Отримайте ідентифікатор Process-Group-групи з будь - якого процесу-ідентифікатора дерева, а не тільки Process-Parent-ID . Варіант того 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)
kill
його викликає процес, що належить до того самого дерева,kill
ризикує вбити себе, перш ніж припинити вбивство всього дерева.> 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
достатньо. Але в разі , коли процес ікра в межах ідентифікатор процесу відрізняється від ID групи .Makefile
Я думаю kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)
, що найкращий простий трюк, щоб убити все дерево процесу, коли його викликають з іншого ідентифікатора групи (іншого дерева процесів).
kill
завжди слід надсилати сигнал усьому дереву, перш ніж отримувати власний сигнал. Але в деяких конкретних обставинах / реалізаціях kill
може надсилати до себе сигнал, бути перерваним і потім отримувати власний сигнал. Однак ризик повинен бути досить мінімальним, і він може бути проігнорований у більшості випадків, оскільки перед цим виникають інші помилки. Чи можна ігнорувати цей ризик у вашому випадку? Більше того, інші відповіді мають цю загальну помилку ( kill
частина процесного дерева вбивається). Сподіваюся, що це допоможе .. Будьте здорові;)
man
це роблять. З іншого боку, якщо ви хочете вбити процес підлітків із дочірнього процесу, kill -- -$pid
не вийде. Тож це не загальне рішення.
child.sh
.
pkill -TERM -P 27888
Це знищить усі процеси, які мають ідентифікатор батьківського процесу 27888.
Або більш надійний:
CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS
який планує вбити 33 секунди пізніше і ввічливо попросити припинити процеси.
Дивіться цю відповідь щодо припинення всіх нащадків.
pkill -P
передає сигнал лише дитині => онук не отримує сигнал => Тому я роздратував ще одну відповідь, щоб пояснити це. Ура ;-)
pkill -TERM -P ${$}
.
$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
Для рекурсивного вбивства дерево процесів використовуйте 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
не працюють на OS X. Щоб зробити його роботу там замінити ps
команду на: ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/g
де _regex
визначаються до for
циклу:local _regex="[ ]*([0-9]+)[ ]+${_pid}"
killtree()
надійність роботи?
ps
не підтримується --ppid
, можна скористатися pgrep -P ${_pid}
натомість
відповідь brad - це те, що я б рекомендував, за винятком того, що ви можете взагалі позбутися awk
, якщо скористаєтесь --ppid
опцією для ps
.
for child in $(ps -o pid -ax --ppid $PPID) do ....... done
якщо ви знаєте, передайте pid батьківського процесу, ось сценарій оболонки, який повинен працювати:
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
Я використовую трохи модифіковану версію описаного тут методу: https://stackoverflow.com/a/5311362/563175
Так виглядає так:
kill `pstree -p 24901 | sed 's/(/\n(/g' | grep '(' | sed 's/(\(.*\)).*/\1/' | tr "\n" " "`
де 24901 - батьківський PID.
Це виглядає досить некрасиво, але це працює ідеально.
pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
-l
до pstree
, так довгі рядки не отримують усічені; це може бути спрощено читати також з kill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`
(не потрібно перетворювати \n
в простір, оскільки це буде добре працювати), thx!
Модифікована версія відповіді жигана:
#!/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
wait $pid
лише в запущених процесах, не у всіх прецесах, тому це не є загальним рішенням
wait
придушить Terminated
повідомлення, якщо це дитина.
Я не можу коментувати (недостатньо репутації), тому змушений додати нову відповідь , хоча це насправді не відповідь.
Існує невелика проблема з інакше дуже приємною і ґрунтовною відповіддю, наданою @olibre 28 лютого. Вихід ps opgid= $PID
буде містити провідні пробіли для PID, менші ніж п’ять цифр, оскільки ps
це виправдовує стовпчик (строго вирівнювання чисел). У всьому командному рядку це призводить до від'ємного знаку, після чого пробілів, а потім групи PID. Просте рішення полягає в трубу , ps
щоб tr
для видалення просторів:
kill -- -$( ps opgid= $PID | tr -d ' ' )
Щоб додати відповідь Нормана Рамзі, можливо, варто поглянути на setid, якщо ви хочете створити групу процесів.
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html
Функція setid () повинна створити новий сеанс, якщо процес виклику не є лідером групи процесів. Після повернення процес виклику повинен бути керівником сесії цієї нової сесії, бути керівником групи процесів нової групи процесів і не мати контрольного терміналу. Ідентифікатор групи процесу процесу виклику встановлюється рівним ідентифікатору процесу процесу виклику. Процес виклику є єдиним процесом у новій групі процесів і єдиним процесом у новому сеансі.
Я вважаю, що ви можете створити групу з початкового процесу. Я використовував це в php, щоб мати змогу вбити ціле дерево процесу після його запуску.
Це може бути поганою ідеєю. Мені були б цікаві коментарі.
Натхненний коментарем ysth
kill -- -PGID
замість того, щоб давати йому номер процесу, надайте йому заперечення номера групи. Як зазвичай з майже будь-якою командою, якщо ви хочете, щоб звичайний аргумент, який починається з а,
-
не інтерпретувався як комутатор, передуйте йому--
PGID
з PID
. Як ти гадаєш? Ура
Наступна функція оболонки схожа на багато інших відповідей, але вона працює як на 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
}
$child
цієї функції, щоб не турбувати інші (не локальні) змінні з тим самим іменем та забезпечити очищення значення локальної змінної після закінчення функції.
Це дуже просто зробити з python за допомогою psutil . Просто встановіть psutil з pip, і тоді у вас є повний набір інструментів для маніпулювання процесом:
def killChildren(pid):
parent = psutil.Process(pid)
for child in parent.get_children(True):
if child.is_running():
child.terminate()
Якщо ви хочете вбити процес на ім'я:
killall -9 -g someprocessname
або
pgrep someprocessname | xargs pkill -9 -g
Це моя версія вбивства всіх дочірніх процесів за допомогою bash script. Він не використовує рекурсії і залежить від команди 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, покладаючись лише на можливості рідного розбору Баша:
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
}
Ура.
Напевно, краще вбити батька перед дітьми; інакше батько, можливо, знову нерестує нових дітей до того, як він сам буде вбитий. Вони переживуть вбивство.
Моя версія PS відрізняється від наведеної вище; можливо, занадто давній, тому дивне гарчання ...
Використання сценарію оболонки замість функції оболонки має багато переваг ...
Однак це в основному ідея жигангів
#!/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). Перший аргумент - батьківський під, від якого дітей потрібно припинити. Другий аргумент - булевий, щоб визначити, чи потрібно також скасувати батьківський під.
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 будь-якому процесу дитини / онука в рамках сценарію оболонки, і якщо SIGTERM не вдасться, він почекатиме 10 секунд, а потім відправить вбитий.
Рання відповідь:
Наступне також працює, але знищить саму оболонку на BSD.
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
Я розробляю рішення жигангу, ксюрі та твердого тіла далі:
#!/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)}'
В основному ідея полягає в тому, що ми збираємо батьківський масив (a): дочірні записи, а потім обводимо навколо масиву пошук дітей для наших відповідних батьків, додаючи їх до нашого списку батьків (p), коли ми йдемо.
Якщо ви не хочете вбивати процес вищого рівня, то робіть
sub(/[0-9]*/, "", p)
безпосередньо перед тим, як рядок system () видалить його з набору убивств.
Майте на увазі, що тут є умова гонки, але це правда (наскільки я бачу) усіх рішень. Він робить те, що мені потрібно, тому що сценарій, який мені потрібен, не створює багато короткочасних дітей.
Вправою для читача було б зробити це циклом з 2 пропусками: після першого проходу надішліть SIGSTOP до всіх процесів у списку p, потім цикл, щоб запустити ps ще раз, а після другого проходу надішліть SIGTERM, потім SIGCONT. Якщо вам не байдуже приємні закінчення, то, мабуть, другий пропуск може бути просто SIGKILL.
Якщо ви знаєте, що ви хочете вбити, ви можете перейти з ідентифікатора сеансу і все в той же сеанс. Я перевірив би двічі, але я використав це для скриптів, що починають rsyncs у циклах, які я хочу померти, а не запускати інший (через цикл), як це було б, якби я просто killall'd rsync.
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid 21709))
Якщо ви не знаєте під, ви все ще можете гніздитися
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid $(pgrep rsync )))
ps -o pid= --ppid $PPID | xargs kill -9
kill -9
справді.
kill -15
не допоможе.
У команді "завдання" відображаються фонові процеси. У деяких випадках може бути краще спочатку вбити найновіший процес, наприклад, старший створив спільний сокет. У цих випадках сортуйте PID у зворотному порядку. Іноді потрібно зачекати момент, коли завдання щось записують на диск або подібні речі, перш ніж вони зупиняться.
І не вбивайте, якщо вам не доведеться!
for SIGNAL in TERM KILL; do
for CHILD in $(jobs -s|sort -r); do
kill -s $SIGNAL $CHILD
sleep $MOMENT
done
done
Вбивство дочірнього процесу в сценарії оболонки:
Багато часу нам потрібно вбити дитячий процес, якого чомусь повісили або перекрили. напр. Проблема з'єднання 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;
і спостерігати за процесами, які мають ім'я як "тест" в іншому терміналі, використовуючи наступну команду.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
Вище написаний сценарій створить 4 нові дочірні процеси та їх батьків. Кожен дочірній процес триватиме 10 сек. Але як тільки час вичерпається в 5 сек, їхні батьківські процеси вбивають цих дітей. Тому дитина не зможе завершити виконання (10 сек). Пограйте в ці таймінги (перемикання 10 і 5), щоб побачити іншу поведінку. У такому випадку дитина закінчить виконання за 5 сек до того, як досягне тайм-ауту 10сек.
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;
і спостерігати за процесами, які мають ім'я як "тест" в іншому терміналі, використовуючи наступну команду.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
Вище написаний сценарій створить 4 нові дочірні процеси. Ми зберігаємо підписи всіх дочірніх процесів і перебираємо їх на певне, щоб перевірити, чи завершено їх виконання чи все ще працює. Дочірній процес буде виконуватися до CMD_TIME часу. Але якщо час очікування закінчиться CNT_TIME_OUT, усі діти будуть вбиті батьківським процесом. Ви можете перемикати таймінг і грати зі сценарієм, щоб побачити поведінку. Одним із недоліків такого підходу є те, що він використовує груповий ідентифікатор для вбивства всього дитячого дерева. Але сам батьківський процес належить до однієї групи, тому він також загине.
Можливо, вам потрібно буде призначити інший ідентифікатор групи для батьківського процесу, якщо ви не хочете, щоб батьки були вбиті.
Більш детальну інформацію можна знайти тут,
Цей сценарій також працює:
#/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
}