쉘 스크립트가 종료 될 때 백그라운드 프로세스 / 작업을 어떻게 종료합니까?


194

최상위 스크립트가 종료 될 때 혼란을 해결하는 방법을 찾고 있습니다.

특히를 사용 set -e하려면 스크립트가 종료되면 백그라운드 프로세스가 종료되기를 바랍니다.

답변:


186

엉망을 정리하기 trap위해 사용할 수 있습니다. 특정 신호가 도착했을 때 실행되는 것들의 목록을 제공 할 수 있습니다.

trap "echo hello" SIGINT

그러나 쉘이 종료되면 무언가를 실행하는 데 사용될 수도 있습니다.

trap "killall background" EXIT

내장되어 있으므로 help trap정보를 제공합니다 (bash와 함께 작동). 백그라운드 작업 만 죽이려면 할 수 있습니다

trap 'kill $(jobs -p)' EXIT

'쉘이 $()즉시 대체되지 않도록 single을 사용 하십시오.


그럼 어떻게 만 아이 를 죽 일까요? (또는 나는 명백한 것을 놓치고있다)
elmarco

18
killall은 당신의 아이들을 죽이지 만 당신은 아닙니다
orip

4
kill $(jobs -p)서브 쉘에서 명령 대체를 실행하기 때문에 대시에서 작동하지 않습니다 (man dash의 명령 대체 참조)
user1431317

8
되는 killall background자리 표시해야하는데? background맨 페이지에 없습니다 ...
Evan Benn

170

이것은 나를 위해 작동합니다 (댓글 작성자 덕분에 개선되었습니다).

trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
  • kill -- -$$전체 프로세스 그룹에 SIGTERM 을 전송하여 하위 항목도 제거합니다.

  • 신호 지정 EXIT은 사용시 유용합니다 set -e(자세한 내용은 여기 참조 ).


1
전체적으로 잘 작동하지만 자식 프로세스는 프로세스 그룹을 변경시킬 수 있습니다. 반면에 작업 제어가 필요하지 않으며 다른 솔루션에서 손자 프로세스를 놓칠 수도 있습니다.
michaeljt

5
"kill 0"은 상위 bash 스크립트도 종료합니다. "kill--$ BASHPID"를 사용하여 현재 스크립트의 자식 만 죽일 수 있습니다. bash 버전에 $ BASHPID가 없으면 BASHPID = $ (sh -c 'echo $ PPID')를 내보낼 수 있습니다.
ACyclic

2
훌륭하고 명확한 해결책에 감사드립니다! 불행히도 트랩 재귀를 허용하는 Bash 4.3을 segfaults합니다. 4.3.30(1)-releaseOSX 에서이 문제가 발생 했으며 Ubuntu 에서도 확인되었습니다 . obvoius wokaround 가 있습니다 :)
skozin

4
잘 모르겠습니다 -$$. '-<PID>`로 평가됩니다 (예 :) -1234. kill 맨 페이지 // 내장 맨 페이지에서 선행 대시는 전송할 신호를 지정합니다. 그러나 아마도 그것을 차단하지만 앞선 대시는 문서화되어 있지 않습니다. 어떤 도움?
Evan Benn

4
@EvanBenn : Check man 2 kill는 PID가 음수 일 때 신호가 제공된 ID ( en.wikipedia.org/wiki/Process_group ) 를 사용하여 프로세스 그룹의 모든 프로세스로 전송됨을 설명합니다 . man 1 kill또는에 언급되지 않았 man bash으며 설명서에서 버그로 간주 될 수 있습니다.
user001

111

업데이트 : https://stackoverflow.com/a/53714583/302079 는 종료 상태 및 정리 기능을 추가하여이를 개선합니다.

trap "exit" INT TERM
trap "kill 0" EXIT

왜 변환 INT하고 TERM종료해야합니까? 모두 kill 0무한 루프에 들어 가지 않고 트리거해야하기 때문 입니다.

왜 방아쇠 kill 0EXIT? 일반 스크립트 엑시트도 트리거해야하기 kill 0때문입니다.

kill 0? 중첩 된 서브 쉘도 종료해야하기 때문입니다. 전체 프로세스 트리 가 다운됩니다 .


3
데비안에 대한 유일한 사례입니다.
MindlessRanger 2016

3
Johannes Schaub의 답변이나 tokland가 제공 한 답변 중 어느 것도 내 데비안에서 쉘 스크립트가 시작된 백그라운드 프로세스를 죽일 수 없었습니다. 이 솔루션은 효과가있었습니다. 이 답변이 더 이상지지되지 않는 이유를 모르겠습니다. 정확히 kill 0의미 / 동작 에 대해 더 확장 할 수 있습니까?
josch

7
이것은 굉장하지만 부모님을 죽입니다 :-(
vidstige

5
이 솔루션은 말 그대로 과잉입니다. kill 0 (내 스크립트 내)이 내 전체 X 세션을 망쳤습니다! 아마도 어떤 경우에는 kill 0이 유용 할 수 있지만, 이것이 일반적인 해결책이 아니라는 사실을 변경하지는 않으며 사용 이유가 없으면 가능한 한 피해야합니다. 스크립트의 백그라운드 작업뿐만 아니라 부모 쉘이나 전체 X 세션을 죽일 수 있다는 경고를 추가하는 것이 좋습니다.
Lissanro Rayen 2012 년

3
@vidstige가 지적한 것처럼 이것은 일부 상황에서 흥미로운 솔루션 일 수 있지만 시작 프로세스 (예 : 대부분의 경우 상위 쉘)를 포함하는 전체 프로세스 그룹 을 종료시킵니다. IDE를 통해 스크립트를 실행할 때 원하는 것이 아닙니다.
matpen

21

트랩 'kill $ (jobs -p)'종료

Johannes의 답변을 약간만 변경하고 jobs -pr을 사용하여 프로세스를 강제 종료하고 목록에 몇 가지 신호를 추가합니다.

trap 'kill $(jobs -pr)' SIGINT SIGTERM EXIT

중지 된 작업도 종료하지 않겠습니까? Bash EXIT 트랩은 SIGINT 및 SIGTERM의 경우에도 실행되므로 이러한 신호의 경우 트랩이 두 번 호출됩니다.
jarno

14

trap 'kill 0' SIGINT SIGTERM EXIT에 설명 된 솔루션 @ tokland의 대답은 정말 좋은이지만, 최신 배쉬는 segmantation 오류와 충돌 을 사용하는 경우. 이는 v. 4.3부터 Bash가 트랩 재귀를 허용하기 때문에이 경우 무한대로됩니다.

  1. 쉘 공정은 접수에 SIGINT하거나 SIGTERM또는 EXIT;
  2. 셸 자체를 포함하여 그룹의 모든 프로세스로 kill 0전송되는 신호가 갇히고 실행 SIGTERM됩니다.
  3. 1로 이동 :)

트랩을 수동으로 등록 해제하여이 문제를 해결할 수 있습니다.

trap 'trap - SIGTERM && kill 0' SIGINT SIGTERM EXIT

더 멋진 방법은 수신 된 신호를 인쇄하고 "종료 :"메시지를 피하는 것입니다.

#!/usr/bin/env bash

trap_with_arg() { # from https://stackoverflow.com/a/2183063/804678
  local func="$1"; shift
  for sig in "$@"; do
    trap "$func $sig" "$sig"
  done
}

stop() {
  trap - SIGINT EXIT
  printf '\n%s\n' "recieved $1, killing children"
  kill -s SIGINT 0
}

trap_with_arg 'stop' EXIT SIGINT SIGTERM SIGHUP

{ i=0; while (( ++i )); do sleep 0.5 && echo "a: $i"; done } &
{ i=0; while (( ++i )); do sleep 0.6 && echo "b: $i"; done } &

while true; do read; done

UPD : 추가 된 최소한의 예; stop불필요한 신호를 제거하고 출력에서 ​​"Terminated :"메시지를 숨기는 기능이 향상되었습니다 . Trevor Boyd Smith 에게 제안 해 주셔서 감사 합니다!


에서 stop()첫 번째 인수를 신호 번호로 제공하지만 등록 취소중인 신호를 하드 코딩합니다. 등록 해제되는 신호를 하드 코딩하는 대신 첫 번째 인수를 사용하여 함수에서 등록을 취소 할 수 있습니다 stop()(그렇게하면 다른 재귀 신호 (하드 코딩 된 3 개 이외))가 중지 될 수 있습니다.
Trevor Boyd Smith

@ TrevorBoydSmith, 이것은 예상대로 작동하지 않을 것입니다. 예를 들어,으로 쉘이 종료 될 수 SIGINT있지만 kill 0send는 SIGTERM다시 한 번 트랩됩니다. 그러나 SIGTERM두 번째 stop호출 중에 트랩 해제 되므로 무한 재귀는 발생하지 않습니다 .
skozin

아마도 trap - $1 && kill -s $1 0더 잘 작동 할 것입니다. 이 답변을 테스트하고 업데이트하겠습니다. 좋은 생각 감사합니다! :)
skozin

아니, trap - $1 && kill -s $1 0우리는 죽일 수 없어서 작동하지 않습니다 EXIT. 그러나 기본적으로이 신호를 보내 TERM므로 de-trap을 실행 하면 충분 kill합니다.
skozin

로 재귀를 테스트 EXIT했으며 trap신호 처리기는 항상 한 번만 실행됩니다.
Trevor Boyd Smith

9

안전한 측면에 있기 위해 정리 기능을 정의하고 트랩에서 호출하는 것이 좋습니다.

cleanup() {
        local pids=$(jobs -pr)
        [ -n "$pids" ] && kill $pids
}
trap "cleanup" INT QUIT TERM EXIT [...]

또는 기능을 완전히 피하십시오.

trap '[ -n "$(jobs -pr)" ] && kill $(jobs -pr)' INT QUIT TERM EXIT [...]

왜? 단순히 trap 'kill $(jobs -pr)' [...]하나만 사용 하면 트랩 조건이 신호 될 때 백그라운드 작업이 실행 된다고 가정하기 때문 입니다 . 작업이 없으면 다음과 같은 메시지가 나타납니다.

kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]

jobs -pr비어 있기 때문에 -나는 그 '트랩'으로 끝났다.


이 테스트 케이스 [ -n "$(jobs -pr)" ]는 내 bash에서 작동하지 않습니다. GNU bash, 버전 4.2.46 (2)-릴리스 (x86_64-redhat-linux-gnu)를 사용합니다. "kill : Usage"메시지가 계속 나타납니다.
Douwe van der Leest

jobs -pr백그라운드 프로세스 하위의 PID를 반환하지 않는다는 사실과 관련이 있다고 생각합니다 . 전체 프로세스 트리를 분해하지 않고 루트 만 제거합니다.
Douwe van der Leest

2

Linux, BSD 및 MacOS X에서 작동하는 멋진 버전입니다. 먼저 SIGTERM을 보내려고 시도하고 성공하지 못하면 10 초 후에 프로세스를 종료합니다.

KillJobs() {
    for job in $(jobs -p); do
            kill -s SIGTERM $job > /dev/null 2>&1 || (sleep 10 && kill -9 $job > /dev/null 2>&1 &)

    done
}

TrapQuit() {
    # Whatever you need to clean here
    KillJobs
}

trap TrapQuit EXIT

작업에는 손자 프로세스가 포함되지 않습니다.



1

또 다른 옵션은 스크립트 자체를 프로세스 그룹 리더로 설정하고 종료시 프로세스 그룹에서 killpg를 트랩하는 것입니다.


프로세스 그룹 리더로서 프로세스를 어떻게 설정합니까? "killpg"는 무엇입니까?
jarno

0

스크립트로드를 스크립트로 작성하십시오. killall스크립트가 완료 되 자마자 실행되는 (또는 OS에서 사용 가능한 것) 명령을 실행하십시오.


0

작업 -p는 서브 쉘에서 호출 된 경우 출력이 파일이 아닌 파일로 경로 재 지정되지 않는 한 모든 쉘에서 작동하지 않습니다. (원래 대화 형으로 만 사용되었다고 가정합니다.)

다음은 어떻습니까 :

trap 'while kill %% 2>/dev/null; do jobs > /dev/null; done' INT TERM EXIT [...]

"작업"에 대한 호출은 데비안의 대시 셸에서 필요하며, 현재 작업 ( "%%")이 누락 된 경우 업데이트하지 못합니다.


흠 흥미로운 접근법이지만 작동하지 않는 것 같습니다. scipt 고려 trap 'echo in trap; set -x; trap - TERM EXIT; while kill %% 2>/dev/null; do jobs > /dev/null; done; set +x' INT TERM EXIT; sleep 100 & while true; do printf .; sleep 1; doneBash (5.0.3)에서 실행하고 종료하려고하면 무한 루프가있는 것 같습니다. 그러나 다시 종료하면 작동합니다. 대시 (0.5.10.2-6)조차도 두 번 종료해야합니다.
jarno

0

포 그라운드 프로세스를 실행하는 경우 트리거되지 않는 것을 알았을 때 @tokland의 답변을 http://veithen.github.io/2014/11/16/sigterm-propagation.html 의 지식과 결합하여 조정했습니다. trap(와 배경이 &아님) :

#!/bin/bash

# killable-shell.sh: Kills itself and all children (the whole process group) when killed.
# Adapted from http://stackoverflow.com/a/2173421 and http://veithen.github.io/2014/11/16/sigterm-propagation.html
# Note: Does not work (and cannot work) when the shell itself is killed with SIGKILL, for then the trap is not triggered.
trap "trap - SIGTERM && echo 'Caught SIGTERM, sending SIGTERM to process group' && kill -- -$$" SIGINT SIGTERM EXIT

echo $@
"$@" &
PID=$!
wait $PID
trap - SIGINT SIGTERM EXIT
wait $PID

작동하는 예 :

$ bash killable-shell.sh sleep 100
sleep 100
^Z
[1]  + 31568 suspended  bash killable-shell.sh sleep 100

$ ps aux | grep "sleep"
niklas   31568  0.0  0.0  19640  1440 pts/18   T    01:30   0:00 bash killable-shell.sh sleep 100
niklas   31569  0.0  0.0  14404   616 pts/18   T    01:30   0:00 sleep 100
niklas   31605  0.0  0.0  18956   936 pts/18   S+   01:30   0:00 grep --color=auto sleep

$ bg
[1]  + 31568 continued  bash killable-shell.sh sleep 100

$ kill 31568
Caught SIGTERM, sending SIGTERM to process group
[1]  + 31568 terminated  bash killable-shell.sh sleep 100

$ ps aux | grep "sleep"
niklas   31717  0.0  0.0  18956   936 pts/18   S+   01:31   0:00 grep --color=auto sleep

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.