Каков наилучший способ отправить сигнал всем членам группы процессов?

Я хочу убить целое дерево процессов. Каков наилучший способ сделать это, используя любые общие языки сценариев? Я ищу простое решение.

30 ответов


вы не говорите, является ли дерево, которое вы хотите убить, одной группой процессов. (Это часто бывает, если дерево является результатом разветвления от запуска сервера или командной строки оболочки.) Вы можете обнаружить группы процессов с помощью 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)

отдельное спасибо танагра и Speakus взносы $PID оставшиеся пробелы и совместимость с OSX.

объяснение

  • kill -9 -"$PGID" => отправить сигнал 9 (KILL) для всех детей и внук...
  • PGID=$(ps opgid= "$PID") => получить Process-Group-ID любой Process-ID дерева, а не только Process-Parent-ID. Вариация ps opgid= $PID is ps -o pgid --no-headers $PID здесь pgid можно заменить на pgrp.
    но:
    • ps вставляет пробелы после PID меньше пяти цифр и выровнены по правому краю, как заметил танагра. Вы можете использовать:
      PGID=$(ps opgid= "$PID" | tr -d ' ')
    • ps из OSX всегда печатайте заголовок, поэтому Speakus предлагает:
      PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"
  • 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 рискует убить себя, прежде чем прекратить убийство всего дерева.
  • поэтому не забудьте запустить команду, используя процесс, имеющий другой Process-Group-ID.

долгая история

> cat run-many-processes.sh
#!/bin/sh
echo "ProcessID=$$ begins ()"
./child.sh background &
./child.sh foreground
echo "ProcessID=$$ ends ()"

> cat child.sh
#!/bin/sh
echo "ProcessID=$$ begins ()"
./grandchild.sh background &
./grandchild.sh foreground
echo "ProcessID=$$ ends ()"

> cat grandchild.sh
#!/bin/sh
echo "ProcessID=$$ begins ()"
sleep 9999
echo "ProcessID=$$ ends ()"

запустите дерево процессов в фоновом режиме с помощью '&'

> ./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 было достаточно. Но в случае, если процесс происходит в пределах a Makefile the идентификатор процесса отличается от ID группы.

я думаю kill -- -$(ps -o pgid= $PID | grep -o [0-9]*) это лучший простой трюк, чтобы убить целое дерево процессов при вызове из другого ID группы (другое дерево процессов).


pkill -TERM -P 27888

Это убьет все процессы, которые имеют идентификатор родительского процесса 27888.

или более надежные:

CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS

который график убийства 33 секунд спустя и вежливо попросить процессы прекратить.

посмотреть ответ для прекращения всех потомков.


чтобы рекурсивно убить дерево процессов, используйте killtree ():

#!/bin/bash

killtree() {
    local _pid=
    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 ) <pid> [signal]"
    exit 1
fi

killtree $@

rkill С программа pslist пакет посылает данный сигнал (или SIGTERM по умолчанию) для указанного процесса и всех его потомков:

rkill [-SIG] pid/name...

бред-это то, что я бы тоже рекомендовал, за исключением того, что вы можете покончить с awk в целом, если вы используете до 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/(\(.*\)).*//' | tr "\n" " "`

где 24901-Родительский 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= 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

чтобы добавить к ответу Нормана Рэмси, возможно, стоит посмотреть на setsid, если вы хотите создать процесс group.
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html

функция setsid() должна создать новый сеанс, если вызывающий процесс не руководитель группы процессов. На возврат вызывающего процесса должен быть лидер сессии нового сессия, будет группой процесса лидером новой группы процессов , и не имеет управляющего терминала. Идентификатор группы процессов вызова процесс устанавливается равным идентификатор вызывающего процесса. Этот вызывающий процесс должен быть единственным процесса в группе процессов и единственный процесс в новом сеансе.

что, как я понимаю, означает, что вы можете создать группу из начального процесса. Я использовал это в php, чтобы иметь возможность убить целое дерево процессов после его запуска.

Это может быть плохо идея. Меня интересуют комментарии.


Я не могу комментировать (не хватает репутации), поэтому я вынужден добавить новый ответ, хотя это на самом деле не ответ.

есть небольшая проблема с в противном случае очень хорошим и тщательным ответом, данным @olibre 28 февраля. Выход ps opgid= $PID будет содержать начальные пробелы для PID короче пяти цифр, потому что ps оправдывает столбец (rigth выравнивает числа). В пределах всей командной строки это приводит к отрицательному знаку, за которым следует пробел(ы), за которым следует группа PID. Простое решение-труба ps to tr удалять пробелы:

kill -- -$( ps opgid= $PID | tr -d ' ' )

вдохновленный комментарий ysth

kill -- -PGID

вместо того, чтобы давать ему номер процесса, дайте ему отрицание группы число. Как обычно с почти любой командой, если вы хотите нормальный аргумент, который начинается с - чтобы не интерпретироваться как переключатель, предшествуйте ему с --


следующая функция оболочки похожа на многие другие ответы, но она работает как на Linux, так и на BSD (OS X и т. д.) без внешних зависимостей, таких как pgrep:

killtree() {
    local parent= child
    for child in $(ps -o ppid= -o pid= | awk "$1==$parent {print $2}"); do
        killtree $child
    done
    kill $parent
}

это очень легко сделать с помощью python, используя psutil. Просто установите psutil с pip, а затем у вас есть полный набор инструментов манипуляции процессом:

def killChildren(pid):
    parent = psutil.Process(pid)
    for child in parent.get_children(True):
        if child.is_running():
            child.terminate()

основываясь на ответе Чжигана, это позволяет избежать самоубийства:

init_killtree() {
    local pid= child

    for child in $(pgrep -P $pid); do
        init_killtree $child
    done
    [ $pid -ne $$ ] && kill -kill $pid
}

Если вы хотите убить процесс по имени:

killall -9 -g someprocessname

или

pgrep someprocessname | xargs pkill -9 -g

Это моя версия убийства всех дочерних процессов с помощью сценария bash. Он не использует рекурсию и зависит от команды pgrep.

использовать

killtree.sh PID SIGNAL

содержание killtrees.sh

#!/bin/bash
PID=
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=

if [ -z $SIGNAL ]
then
    SIGNAL="TERM"
fi
#do substring from comma to space
kill -$SIGNAL ${PPLIST//,/ }

вот вариант ответа @zhigang, который делает без AWK, полагаясь только на собственные возможности синтаксического анализа Bash:

function killtree {
  kill -STOP ""
  ps -e -o pid= -o ppid= | while read -r pid ppid
                           do
                             [[ $ppid =  ]] || continue
                             killtree "$pid"  || true # Skip over failures
                           done
  kill -CONT ""          
  kill -TERM ""
}

похоже, он отлично работает как на Mac, так и на Linux. В ситуациях, когда вы не можете полагаться на возможность управлять группами процессов-например, при написании сценариев для тестирования программного обеспечения, которое должно быть построено в нескольких средах, - эта техника ходьбы по деревьям определенно полезна.


вероятно, лучше убить родителя перед детьми; в противном случае родитель может, вероятно, снова породить новых детей, прежде чем он сам убьет. Они переживут убийство.

моя версия ps отличается от приведенной выше; возможно, слишком старая, поэтому странная grepping...

использование сценария оболочки вместо функции оболочки имеет много преимуществ...

однако, это в основном идея zhigangs


#!/bin/bash
if test $# -lt 1 ; then
    echo >&2 "usage: kiltree pid (sig)"
fi ;

_pid=
_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=""
        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=""
    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=
_sig=${2:-TERM}

# echo >&2 "killtree($_pid) mypid = $$"
# ps axwwf | grep -6 "^[ ]*$_pid " >&2 ;

function _killtree () {
    local _children
    local _child
    local _success

    if test  -eq  ; 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  ;

    _children=$(ps -o pid --no-headers --ppid ) ;        
    _success=0 
    for _child in ${_children}; do
        _killtree  ${_child}  ;
        _success=$(($_success+$?)) ;
      done ;

    if test $_success -eq 0 ; then
        kill - 
      fi ;
    # when a stopped process is killed, it will linger in the system until it is continued
    kill -SIGCONT 
    test $_success -eq 0 ;
    return $?
    }

_killtree $$ $_pid $_sig

эта версия будет избегать убийства своей родословной , что вызывает поток дочерних процессов в предыдущих решениях.

процессы правильно останавливаются перед определением дочернего списка, так что новые дочерние элементы не создаются и не исчезают.

после смерти остановленные задания должны продолжать исчезать из системы.


старый вопрос, я знаю, но все ответы, кажется, продолжают называть ps, что мне не понравилось.

это решение на основе awk не требует рекурсии и вызывает ps только один раз.

awk 'BEGIN {
  p=1390
  while ("ps -o ppid,pid"|getline) a[]=a[]" "
  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[]=a[]" ";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) записей parent:child, а затем обводим массив, находя детей для наших соответствующих родителей, добавляя их в список родителей (p) по мере продвижения.

Если вы не хотите убейте процесс верхнего уровня, а затем сделайте

sub(/[0-9]*/, "", p)

непосредственно перед тем, как строка system () удалит ее из набора убийств.

имейте в виду, что здесь есть расовое условие, но это верно (насколько я могу видеть) для всех решений. Он делает то, что мне нужно, потому что сценарий, для которого он нужен, не создает много недолговечных детей.

упражнение для читателя было бы сделать его циклом 2-pass: после первого прохода отправьте SIGSTOP всем процессам в список p, затем цикл для запуска ps снова и после второго прохода отправить SIGTERM, затем SIGCONT. Если вы не заботитесь о хороших концовках, то второй проход может быть просто SIGKILL, я полагаю.


Я знаю, что это старый, но это лучшее решение, которое я нашел:

killtree() { 
    for p in $(pstree -p  | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do
        echo Terminating: $p 
        kill $p
    done
}

Спасибо за вашу мудрость, люди. Мой скрипт оставлял некоторые дочерние процессы на выходе и отрицание совет сделал вещи легче. Я написал эту функцию для использования в других скриптах при необходимости:

# 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  in
        -x) [ -n "" ] && kill -9 - || kill -9 -$$ ;;
        "") mainpid=$$ ;;
         *) mainpid= ;;
    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)'

Если вы знаете pid вещи, которую хотите убить, вы обычно можете перейти от идентификатора сеанса и всего в том же сеансе. Я бы дважды проверил, но я использовал это для сценариев, запускающих rsyncs в циклах, которые я хочу умереть, а не запускать другой (из-за цикла), как если бы я просто убил 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 )))

ps -o pid= --ppid $PPID | xargs kill -9 

в sh команда jobs отобразит фоновые процессы. В некоторых случаях может быть лучше сначала убить новейший процесс, например, более старый создал общий сокет. В этих случаях сортируйте PIDs в обратном порядке. Иногда вы хотите дождаться момента, когда задания напишут что-то на диске или что-то в этом роде, прежде чем они остановятся.

и не убивайте, если вам не нужно!

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 секунд. Но как только тайм-аут 5sec достигнет, их соответствующие родительские процессы убьют этих детей. Таким образом, ребенок не сможет завершить выполнение(10sec). Играть вокруг этих таймингов(переключатель 10 и 5), чтобы увидеть другое поведение. В этом случае ребенок завершит выполнение в 5sec, прежде чем он достигнет таймаута 10sec.

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 новых дочерних процесса. Мы храним pids всего дочернего процесса и зацикливаемся на них, чтобы проверить, они закончили свое исполнение или все еще работают. Дочерний процесс будет выполняться до времени CMD_TIME. Но если время ожидания CNT_TIME_OUT достигнет, все дети будут убиты родительским процессом. Вы можете переключать время и играть со сценарием, чтобы увидеть поведение. Одним из недостатков этого подхода является использование идентификатора группы для уничтожения всего дочернего дерева. Но сам родительский процесс принадлежит к той же группе, поэтому он также будет убит.

необходимо назначить другой 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 ' == '$ppid' { print }'` do echo killing $i kill $i done done