Ctrl+C를 누를 때 쉘 스크립트에서 백그라운드 프로세스가 완료될 때까지 어떻게 종료하고 기다리나요?

Ctrl+C를 누를 때 쉘 스크립트에서 백그라운드 프로세스가 완료될 때까지 어떻게 종료하고 기다리나요?

Ctrlc백그라운드 프로세스를 실행하고 쉘 스크립트를 사용할 때 하위 프로세스를 종료한 다음 종료되도록 쉘 스크립트를 설정하려고 합니다 .

내가 생각할 수 있는 최선은 이것이다. kill 0 -INT대기가 발생하기 전에 스크립트도 종료되는 것으로 나타나 므로 쉘 스크립트는 하위 프로세스가 완료되기 전에 종료됩니다.

이 쉘 스크립트가 전송된 후 자식이 죽을 때까지 기다리게 하는 방법에 대한 아이디어가 있습니까 INT?

#!/bin/bash
trap 'killall' INT

killall() {
    echo "**** Shutting down... ****"
    kill 0 -INT
    wait # Why doesn't this wait??
    echo DONE
}

process1 &
process2 &
process3 &

cat # wait forever

답변1

당신의 kill명령은 거꾸로입니다.

많은 UNIX 명령과 마찬가지로 빼기 기호로 시작하는 옵션은 다른 매개변수 앞에 나타나야 합니다.

당신이 쓴다면

kill -INT 0

-INT옵션 으로 처리되어 SIGINT( 현재 프로세스 그룹의 모든 프로세스를 나타내는 특수 번호) 0로 전송됩니다.0

하지만 쓰다보면

kill 0 -INT

을 보고 0더 이상 옵션이 없다고 판단하여 SIGTERM기본값을 사용합니다. 그리고 보내다저것당신이 그랬던 것처럼 현재 프로세스 그룹에

kill -TERM 0 -INT    

SIGTERM(또한 에 보내려고 시도하는데 -INT이로 인해 구문 오류가 발생하지만 먼저 SIGTERM에 보내고 0그 이상 도달하지 않습니다.)

따라서 기본 스크립트 는 SIGTERM실행 wait하기 전에 및 echo DONE.

다음에 추가

trap 'echo got SIGTERM' TERM

맨 위, 바로 뒤에

trap 'killall' INT

그것을 증명하기 위해 다시 실행했습니다.

Stephane Chazelas가 지적했듯이 배경(등)이 있는 자녀는 기본적으로 무시 process1됩니다 .SIGINT

어쨌든 보내는 것이 SIGTERM더 합리적이라고 생각합니다.

결국 kill -process group아이들에게 먼저 준다고 장담할 수는 없을 것 같습니다. 문을 닫을 때는 신호를 무시하는 것이 좋습니다.

그러니 이것을 시도해 보세요:

#!/bin/bash
trap 'killall' INT

killall() {
    trap '' INT TERM     # ignore INT and TERM while shutting down
    echo "**** Shutting down... ****"     # added double quotes
    kill -TERM 0         # fixed order, send TERM not INT
    wait
    echo DONE
}

./process1 &
./process2 &
./process3 &

cat # wait forever

답변2

불행히도 백그라운드에서 시작된 명령은 SIGINT를 무시하도록 셸에 의해 설정되며 더 나쁜 것은 작동하지 않는다는 것 trap입니다.

(trap - INT; exec process1) &
(trap - INT; exec process2) &
trap '' INT
wait

Ctrl-C를 누르면 process1과 process2가 동일한 프로세스 그룹(즉, 터미널의 전경 프로세스 그룹)에 속하기 때문에 SIGINT를 수신하기 때문입니다.

위 코드는 이와 관련하여 POSIX와 호환되지 않는 pdksh 및 zsh와 함께 사용됩니다.

다른 셸의 경우 다음과 같은 다른 방법을 사용하여 SIGINT에 대한 기본 처리기를 복원해야 합니다.

perl -e '$SIG{INT}=DEFAULT; exec "process1"' &

또는 SIGTERM과 같은 다른 신호를 사용하십시오.

답변3

일부 백그라운드 프로세스를 관리하고 싶다면 bash 작업 제어 기능을 사용해 보는 것은 어떨까요?

$ gedit &
[1] 2581
$ emacs &
[2] 2594
$ jobs
[1]-  Running                 gedit &
[2]+  Running                 emacs &
$ jobs -p
2581
2594
$ kill -2 `jobs -p`
$ jobs
[1]-  Interrupt               gedit
[2]+  Done                    emacs

답변4

그래서 그것도 수정했어요. Bash에서는 함수를 정의하고 이를 사용하여 흥미로운 작업을 수행할 수 있습니다. 나와 동료들은 terminator이를 일괄 실행에 사용합니다. 출력을 보려면 일반적으로 여러 개의 종료자 창을 생성합니다( tmux탭만큼 우아하지는 않지만 GUI와 유사한 인터페이스를 사용할 수 있습니다. 하하).

제가 생각해낸 설정과 실행할 수 있는 예는 다음과 같습니다.

#!/bin/bash
custom()
{
    terun 'something cool' 'yes'
    run 'xclock'
    terun 'echoes' 'echo Hello; echo Goodbye; read'
    func()
    {
        local i=0
        while :
        do
            echo "Iter $i"
            let i+=1
            sleep 0.25
        done
    }
    export -f func
    terun 'custom func' 'func'
}

# [ Setup ]
run()
{
    "$@" &
    # Give process some time to start up so windows are sequential
    sleep 0.05
}
terun()
{
    run terminator -T "$1" -e "$2"
}
finish()
{
    procs="$(jobs -p)"
    echo "Kill: $procs"
    # Ignore process that are already dead
    kill $procs 2> /dev/null
}
trap 'finish' 2

custom

echo 'Press <Ctrl+C> to kill...'
wait

실행해

$ ./program_spawner.sh 
Press <Ctrl+C> to kill...
^CKill:  9835
9840
9844
9850
$ 

편집하다 @Maxim 님, 방금 귀하의 제안을 보았는데 훨씬 쉬워졌습니다! 감사해요!

관련 정보