프로세스 그룹의 모든 구성원에게 신호를 보내는 가장 좋은 방법은 무엇입니까?


422

전체 프로세스 트리를 죽이고 싶습니다. 일반적인 스크립팅 언어를 사용하여이를 수행하는 가장 좋은 방법은 무엇입니까? 간단한 해결책을 찾고 있습니다.


3
시스템 리퍼가 작동하면 좀비가 사라집니다. 좀비가 남아있는 시스템을 보았지만 비정형 적입니다.
Brian Knoblauch

7
때때로 좀비가 남아있는 사람들은 무서운 활동을 담당합니다.
사용자

chronos또는 herodes명령 중 하나를 사용하십시오 .
Michael Le Barbier Grünewald

8
kill $ (pstree <PID> -p -a -l | cut -d, -f2 | cut -d ''-f1)
vrdhn

@ MichaelLeBarbierGrünewald이 프로그램들에 링크 해 주시겠습니까?
스티로폼은

답변:


305

죽이고 자하는 트리가 단일 프로세스 그룹인지는 말할 수 없습니다. (트리가 서버 시작 또는 쉘 명령 행에서 분기 된 결과 인 경우가 종종 있습니다.) 다음과 같이 GNU ps를 사용하여 프로세스 그룹을 발견 할 수 있습니다.

 ps x -o  "%p %r %y %x %c "

강제 종료하려는 프로세스 그룹 인 경우 kill(1)명령을 사용 하지만 프로세스 번호를 지정하는 대신 그룹 번호를 무시 하십시오. 예를 들어 그룹 5112의 모든 프로세스를 종료하려면을 사용하십시오 kill -TERM -- -5112.


3
kill -74313 -bash : kill : 74313 : 잘못된 신호 사양 kill -15 -GPID를 추가하면 완벽하게 작동합니다.
Adam Peck 2016

51
당신이 정상적인 인수를 원하는 경우 거의 모든 명령과 함께 평소와 같이,로 시작는 것을 - 스위치로 해석 할 수 없습니다에, 그것을 앞에 - : 죽 - -GPID
ysth

9
pgrep프로세스 그룹 ID를보다 쉽게 ​​찾을 수 있습니다. 예를 들어 my-script.sh의 프로세스 그룹을 종료하려면을 실행하십시오 kill -TERM -$(pgrep -o my-script.sh).
Josh Kelley

9
stackoverflow.com/questions/392022/…를 더 잘 살펴보십시오. 훨씬 더 우아한 솔루션이며 어린이의 pid를 나열 해야하는 경우 다음을 사용하십시오.ps -o pid --no-headers --ppid $PARENT_PID
Szymon Jeż

4
그리고 형식을 약간 수정하고 정렬하면 모든 프로세스가 멋지게 그룹화되고 각 그룹의 그룹 부모로 시작하여 (잠재적으로) 볼 수 있습니다.ps x -o "%r %p %y %x %c" | sort -nk1,2
haridsv

199

프로세스 그룹 ID ( )를 사용하여 동일한 프로세스 트리에 속하는 모든 프로세스를 종료하십시오.PGID

  • kill -- -$PGID     기본 신호 사용 ( TERM= 15)
  • kill -9 -$PGID     신호를 사용하십시오 KILL(9)

동일한 프로세스 트리Process-ID ( ) PGID에서 를 검색 할 수 있습니다.PID

  • kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*')   (신호 TERM)
  • kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*')   (신호 KILL)

남은 공간과 OSX 호환성 에 기여한 tanagerSpeakus 에게 특별한 감사를드립니다 $PID.

설명

  • kill -9 -"$PGID"=> KILL모든 어린이와 손자 에게 신호 9 ( )를 보냅니다 .
  • PGID=$(ps opgid= "$PID")=> Process-Parent-ID 뿐만 아니라 트리의 모든 Process -ID 에서 Process-Group-ID 를 검색합니다 . 의 변형 IS 에 의해 대체 될 수있다 . 그러나: ps opgid= $PIDps -o pgid --no-headers $PIDpgidpgrp
    • pstanager에서 알 수PID 있듯이 5 자리 미만이고 오른쪽으로 정렬 되면 선행 공백을 삽입합니다 . 당신이 사용할 수있는:
      PGID=$(ps opgid= "$PID" | tr -d ' ')
    • psOSX에서 항상 헤더를 인쇄하므로 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)

한정

  • davideHubert Kario가 알 수 있듯이 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

결론

I는이 예에서 통지 PID하고 PGID(동일하다 28957).
이것이 내가 처음 kill -- -$PID에는 충분 하다고 생각한 이유 입니다. 그러나 경우에 프로세스는 내 산란되는 프로세스 ID가 로부터 다른 그룹 ID .Makefile

kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)다른 그룹 ID (다른 프로세스 트리) 에서 호출 될 때 전체 프로세스 트리를 죽이는 가장 간단한 트릭 이라고 생각 합니다 .


1
안녕하세요 @davide. 좋은 질문. 나는 kill자신의 신호를 받기 전에 항상 전체 트리에 신호를 보내야 한다고 생각 합니다. 그러나 일부 특정 상황 / 구현 kill에서 신호를 자신에게 보내고 중단 한 다음 자체 신호를 수신 할 수 있습니다. 그러나 위험은 충분히 최소화되어야하며, 다른 버그가이 버그보다 먼저 발생하기 때문에 대부분의 경우 무시 될 수 있습니다. 귀하의 경우이 위험을 무시할 수 있습니까? 또한 다른 답변에는이 일반적인 버그가 있습니다 ( kill프로세스 트리의 일부가 종료 됨). 도움이
되길

3
하위 명령 자체가 그룹 리더가 아닌 경우에만 작동합니다. 그런 간단한 도구조차 man그렇게합니다. 반면에 자식 프로세스에서 Gandchild 프로세스를 죽이려면 kill -- -$pid작동하지 않습니다. 따라서 일반적인 해결책이 아닙니다.
허버트 카리오

1
예를 들어 "자식"이 자식을 죽이려고합니다 (사용자의 손자가 명령을 시작 함). IOW에서에서 백그라운드 프로세스 계층 구조를 종료하십시오 child.sh.
허버트 카리오

1
> 명 -QUIT - "$ PGID"# 동일한 신호 같이 CRTL + C]는 키보드 ---- 진실한 INT로 대체되어야 QUIT
맥심 Kholyavkin

1
OSX 옵션 --no-headers는 지원되지 않으므로 코드를 다음과 같이 업데이트해야합니다. PGID = "$ (ps -o pgid"$ PID "| grep [0-9] | tr -d '')"
Maxim Kholyavkin

166
pkill -TERM -P 27888

부모 프로세스 ID가 27888 인 모든 프로세스가 종료됩니다.

또는 더 강력한 :

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

33 초 후에 살해를 계획하고 프로세스 종료를 정중하게 요청합니다.

모든 자손을 종료하려면 이 답변 을 참조하십시오 .


19
빠른 테스트에서 pgrep은 직계 자식 만보고 했으므로 전체 계층 구조가 종료되지 않을 수 있습니다.
haridsv

10
나는 @haridsv에 동의합니다 : pkill -P신호를 아이에게만 보냅니다 => 손자는 신호를받지 않습니다 => 따라서 나는 그것을 설명하는 또 다른 대답 을 썼습니다 . 건배 ;-)
올리 비

1
bash 스크립트에서 자신의 자식을 죽이려면을 사용하십시오 pkill -TERM -P ${$}.
François Beausoleil

3
@Onlyjob, 자고 죽이는 것이 위험하지 않습니까? 그동안 프로세스 ID가 OS에서 재사용되었을 수 있습니다. 더 이상 자녀가 아닌 프로세스를 종료 할 수 있습니다. 이를 방지하기 위해 pkill에 대한 호출이 다시 수행되어야한다고 생각합니다.
François Beausoleil

2
@Onlyjob 참고로, 컴퓨터에서 19 초 이내에 가벼운 I / O 액세스로 32768 개의 프로세스를 단일 스레드로 생성 할 수 있습니다.$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
cychoi

102

프로세스 트리를 재귀 적으로 종료하려면 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 $@

3
--인수하기 ps는 대체가 작동하도록 OS X의에없는 일을 ps:에 의해 명령을 전과 정의 : 루프ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/g_regexforlocal _regex="[ ]*([0-9]+)[ ]+${_pid}"
아르투르

5
중지 된 프로세스는 SIGTERM으로 종료되지 않습니다. 내 답변
x-yuri

1
-1은 #! / usr / bin / env bash 대신 #! / bin / bash를 사용합니다 (또는 POSIX 만 구문과 / bin / sh 만 사용하는 것이 좋습니다)
Good Person

killtree()안정적으로 작동 하도록 SIGTERM 대신 SIGKILL을 보내면 충분 합니까?
davide

3
경우 ps지원하지 않는 --ppid한 사용할 수 있습니다 pgrep -P ${_pid}대신에
휴 버트 Kario

16

pslist 패키지의 rkill 명령은 지정된 신호 (또는SIGTERM기본적으로)를 지정된 프로세스 및 모든 하위 항목으로보냅니다.

rkill [-SIG] pid/name...

11

브래드의 대답은 내가 너무 좋습니다 당신이 멀리 할 수 있다는 점을 제외하고 줄 것입니다 awk당신이 사용하는 모두 경우 --ppid에 옵션을 ps.

for child in $(ps -o pid -ax --ppid $PPID) do ....... done

어떤 이유로 (Centos5) -ax를 꺼내지 않으면 나에게 도움이되지 않습니다. 그렇지 않으면 이것은 좋습니다!
xitrium

9

부모 프로세스의 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

"ax"대신 "-ax"를 사용하면 일부 버전의 ps에서 경고가 표시됩니다. 따라서 : $의 자녀 (PS -o PID, PPID 도끼 | \ AWK "{경우 (\ $ 2 == $ PID) {인쇄 \ $ 1}}")
코리 R. 킹

9

여기에 설명 된 약간 수정 된 버전의 메소드를 사용합니다. https://stackoverflow.com/a/5311362/563175

따라서 다음과 같습니다.

kill `pstree -p 24901 | sed 's/(/\n(/g' | grep '(' | sed 's/(\(.*\)).*/\1/' | tr "\n" " "`

여기서 24901은 부모의 PID입니다.

꽤 추한 것처럼 보이지만 완벽하게 작동합니다.


1
sed 대신 grep로 단순화하는 중 ...pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
anishsane

2
당신은 추가해야합니다 -lpstree너무 긴 줄이 잘릴 해달라고; 그것은 더 잘 읽을 수 있습니다 kill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`( \n정확하게 작동하기 때문에 공간으로 변환 할 필요가 없습니다 ), thx!
물병 자리 힘

9

zhigang의 답변의 수정 된 버전 :

#!/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이 일반적인 솔루션되지 않도록 프로세스 만에 당신은, 모든 세차 운동을 시작했습니다
휴 버트 Kario

@Hubert Kario이 경우 대기는 0이 아닌 상태로 종료하고 스크립트 실행을 계속합니다. 내가 잘못? 그러나 wait표시되지 않습니다 Terminated그것은 아이의 경우 메시지를.
x-yuri

9

나는 (평판이 충분하지 않은) 의견을 말할 수 없으므로 이것이 실제로 답변이 아니지만 새로운 답변 을 추가해야합니다 .

2 월 28 일에 @olibre가 제공 한 매우 훌륭하고 철저한 대답에는 약간의 문제 ps opgid= $PID가 있습니다 . 결과 ps는 열을 정당화 하기 때문에 5 자리보다 짧은 PID의 선행 공백을 포함 합니다 (숫자를 정렬합니다). 전체 명령 행에서 음수 부호, 공백, 그룹 PID가 차례로 나타납니다. 간단한 해결책은 공간을 제거 하기 위해 파이프 ps하는 tr것입니다.

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

감사합니다. 나는 내 대답을 고칠 것이다;) 건배
올리버

8

Norman Ramsey의 답변에 추가하려면 프로세스 그룹을 작성하려는 경우 setsid를 살펴볼 가치가 있습니다.
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html

호출 프로세스가 프로세스 그룹 리더가 아닌 경우 setsid () 함수는 새 세션을 작성해야합니다. 돌아온 후, 호출 프로세스는이 새로운 세션의 세션 리더가되며, 새로운 프로세스 그룹의 프로세스 그룹 리더가되며 제어 터미널이 없어야합니다. 호출 프로세스의 프로세스 그룹 ID는 호출 프로세스의 프로세스 ID와 동일하게 설정해야합니다. 호출 프로세스는 새로운 프로세스 그룹의 유일한 프로세스이고 새로운 세션의 유일한 프로세스입니다.

시작 프로세스에서 그룹을 만들 수 있음을 의미합니다. 나는 그것을 시작한 후 전체 프로세스 트리를 죽일 수 있도록 PHP에서 이것을 사용했습니다.

이것은 나쁜 생각 일 수 있습니다. 의견에 관심이 있습니다.


실제로 이것은 좋은 생각이며 잘 작동합니다. 프로세스를 동일한 프로세스 그룹에 넣을 수있는 경우 (또는 같은 그룹에 이미 있습니다) 사용하고 있습니다.
sickill

7

ysth의 의견에서 영감을 받음

kill -- -PGID

프로세스 번호를 부여하는 대신 그룹 번호를 무시하십시오. 거의 모든 명령에서와 마찬가지로 a -로 시작하는 일반 인수를 스위치로 해석하지 않으려면 앞에--


죄송합니다 . 귀하 와 동일한 답변 을하셨습니다 => +1. 그러나 또한 단순히 얻는 방법을 설명 PGID에서 PID. 어떻게 생각해? 건배
올리버

5

다음 쉘 함수는 다른 많은 답변과 비슷하지만 외부 종속성없이 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
}

두 번째 줄 끝에 "자식"이라는 단어가 추가 된 것 같습니다.
mato

@mato – 추가가 아닙니다. $child동일한 이름을 가진 다른 (로컬이 아닌) 변수를 방해하지 않고 함수가 종료 된 후에 로컬 변수의 값이 정리되도록 해당 함수 의 범위를 제한합니다 .
Adam Katz

오, 지금은 그것이 과제의 일부라고 생각했습니다. 의견을 작성하기 전에 다시 읽어야합니다. :) 감사.
mato

Btw, 어린이 목록을 만든 다음 맨 위에서 부모를 죽이는 것이 낫지 않습니까? .. 따라서 부모가 자식을 재생성하거나 의도 된 행동을 변경시킬 수있는 다음 코드를 계속 실행하는 상황을 피할 수 있습니다.
mato

5

psutil을 사용하여 파이썬 으로이 작업을 수행하는 것은 매우 쉽습니다 . psutil을 pip와 함께 설치하면 전체 프로세스 조작 도구 세트가 있습니다.

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

5

zhigang의 답변에 따르면, 이것은 자살을 피합니다.

init_killtree() {
    local pid=$1 child

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

4

이름으로 프로세스를 종료하려면 다음을 수행하십시오.

killall -9 -g someprocessname

또는

pgrep someprocessname | xargs pkill -9 -g

3

이것은 bash 스크립트를 사용하여 모든 자식 프로세스를 죽이는 버전입니다. 재귀를 사용하지 않으며 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//,/ }

1
하위 목록이 작성된 후 새 프로세스가 작성되면 실패합니다.
ceving

3

다음은 BW의 기본 구문 분석 가능성에만 의존하는 AWK가없는 @zhigang의 대답 변형입니다.

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 모두에서 잘 작동하는 것 같습니다. 여러 환경에서 빌드해야하는 소프트웨어를 테스트하기 위해 스크립트를 작성할 때와 같이 프로세스 그룹을 관리 할 수없는 상황에서이 트리 워킹 기술은 확실히 도움이됩니다.


1

여러분의 지혜에 감사드립니다. 내 스크립트는 종료시 일부 자식 프로세스를 남겨두고 부정 팁으로 작업을 쉽게했습니다. 필요한 경우 다른 스크립트에서 사용되도록이 함수를 작성했습니다.

# 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
}

건배.


1

시스템에 pstree 및 perl이 있으면 다음을 시도하십시오.

perl -e 'kill 9, (`pstree -p PID` =~ m/\((\d+)\)/sg)'

1

자녀보다 먼저 부모를 죽이는 것이 좋습니다. 그렇지 않으면 부모는 새로운 아이들이 자신을 죽이기 전에 다시 산란 할 수 있습니다. 이들은 살해에서 살아남을 것입니다.

내 ps 버전은 위와 다릅니다. 어쩌면 너무 오래되어 이상한 잡기 ...

쉘 함수 대신 쉘 스크립트를 사용하면 많은 장점이 있습니다 ...

그러나 기본적으로 zhigangs 아이디어입니다.


#!/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

@zhighang 스크립트는 상위 프로세스를 SIGSTOPs하고 신호를 중지 된 프로세스에 전달하므로, AFAIK는 하위 프로세스를 생성하는 프로세스와 신호 전달간에 경쟁 조건을 유발하지 않아야합니다. 그러나 버전에는 자녀 목록을 얻는 것과 부모에게 신호를 전달하는 것 사이에 경쟁이 있습니다.
허버트 카리오

1

다음은 FreeBSD, Linux 및 MacOS X에서 테스트되었으며 pgrep 및 kill에만 의존합니다 (ps -o 버전은 BSD에서 작동하지 않음). 첫 번째 주장은 자녀를 끝내야하는 부모 pid입니다. 두 번째 인수는 부모 pid도 종료해야하는지 여부를 결정하는 부울입니다.

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 초간 기다렸다가 kill을 보냅니다.


조기 답변 :

다음은 작동하지만 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

1

zhigang, xyuri 및 solidsneck의 솔루션을 추가로 개발합니다.

 #!/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

이 버전은 이전 솔루션에서 하위 프로세스가 과도하게 발생하는 조상을 제거하지 않습니다.

하위 목록이 결정되기 전에 프로세스가 올바르게 중지되므로 새 하위 항목이 작성되거나 사라지지 않습니다.

종료 된 후에는 중지 된 작업이 시스템에서 계속 사라져야합니다.


1

오래된 질문이지만 알고 있지만 모든 응답은 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 () 행이 kill set에서 제거되기 직전에.

여기에는 경쟁 조건이 있다는 것을 명심하십시오. 그러나 모든 솔루션에 대해 (내가 볼 수있는 한) 사실입니다. 내가 필요로하는 스크립트가 많은 단기 아동을 만들지 않기 때문에 필요한 작업을 수행합니다.

독자는 2 패스 루프로 만들어야합니다. 첫 번째 패스 후 SIGSTOP을 p 목록의 모든 프로세스로 보낸 다음, ps를 다시 실행하기 위해 루프하고 두 번째 패스는 SIGTERM을 보낸 다음 SIGCONT를 보내십시오. 좋은 결말에 신경 쓰지 않는다면 두 번째 패스는 SIGKILL 일 수 있습니다.


0

당신이 죽이고 싶은 것의 pid를 알고 있다면, 일반적으로 세션 ID와 같은 세션의 모든 것에서 벗어날 수 있습니다. 나는 이중 검사를하지만, 내가 죽을 루프에서 rsyncs를 시작하는 스크립트에 이것을 사용했고, 단지 rall을했을 때와 마찬가지로 (루프 때문에) 다른 것을 시작하지는 않았다.

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 )))


0

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

0

쉘 스크립트에서 자식 프로세스 종료

많은 시간 동안 우리는 어떤 이유로 중단되거나 차단 된 자식 프로세스를 종료해야합니다. 예. 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)을 재생하십시오. 이 경우 자식은 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" '

위의 스크립트는 4 개의 새로운 자식 프로세스를 만듭니다. 우리는 모든 자식 프로세스의 pid를 저장하고 반복하여 실행이 완료되었거나 여전히 실행 중인지 확인합니다. 자식 프로세스는 CMD_TIME 시간까지 실행됩니다. 그러나 CNT_TIME_OUT 시간 초과에 도달하면 모든 하위 프로세스가 상위 프로세스에 의해 종료됩니다. 타이밍을 전환하고 스크립트를 사용하여 동작을 볼 수 있습니다. 이 접근법의 한 가지 단점은 모든 자식 트리를 죽이기 위해 그룹 ID를 사용한다는 것입니다. 그러나 상위 프로세스 자체는 동일한 그룹에 속하므로 종료됩니다.

부모를 죽이지 않으려면 다른 그룹 ID를 부모 프로세스에 할당해야 할 수도 있습니다.

자세한 내용은 여기를 참조하십시오.

쉘 스크립트에서 자식 프로세스 종료


0

이 스크립트는 다음과 같이 작동합니다.

#/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


0

나는 그것이 오래되었다는 것을 알고 있지만 그것이 내가 찾은 더 좋은 해결책입니다.

killtree() { 
    for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do
        echo Terminating: $p 
        kill $p
    done
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.