Ctrl + C를 할 때 쉘 프로세스에서 백그라운드 프로세스가 종료되고 종료 될 때까지 어떻게 대기 할 수 있습니까?


24

백그라운드 프로세스를 실행하도록 쉘 스크립트를 설정하려고하는데 쉘 스크립트 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


"shell intercept kill switch"를 검색 하여이 질문을 발견했습니다. 트랩 명령을 공유해 주셔서 감사합니다. 닫기 GUI 버튼을 통해 앱이 올바르게 종료되지 않으면 Ctrl + C로 앱을 종료 한 후 명령을 실행하는 것이 매우 유용합니다.
baptx

답변:


22

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

많은 UNIX 명령과 마찬가지로 마이너스로 시작하는 옵션이 다른 인수보다 먼저 나와야합니다.

당신이 쓰는 경우

kill -INT 0

-INT옵션으로 보고 전송 SIGINT합니다 0( 0현재 프로세스 그룹의 모든 프로세스를 의미하는 특수 번호).

하지만 글을 쓰면

kill 0 -INT

이 표시되고 0더 이상 옵션이 없다고 결정하므로 SIGTERM기본적으로 사용 됩니다. 그리고 보내는 것을 당신이 한 것과 같은, 현재 프로세스 그룹에

kill -TERM 0 -INT    

(또한 전송 시도 할 것이다 SIGTERM-INT구문 오류가 발생할 것이다, 그러나 그것은 전송 SIGTERM0첫번째, 멀리 것을 얻을 수 없다.)

따라서 주 스크립트는 and SIGTERM를 실행하기 전에를 받고 있습니다.waitecho 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

고마워,이 작동합니다! 문제가 발생한 것 같습니다.
17:43에

9

불행히도 백그라운드에서 시작된 명령은 SIGINT를 무시하도록 쉘에 의해 설정되며 더 나쁜 것은 무시할 수 없습니다 trap. 그렇지 않으면, 당신이 할 일은

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

process1과 process2는 터미널의 포 그라운드 프로세스 그룹과 동일한 프로세스 그룹의 일부이므로 Ctrl-C를 누르면 SIGINT를 얻습니다.

위의 코드는 pdksh 및 zsh와 작동하며 POSIX 호환이 아닙니다.

다른 쉘에서는 다음과 같이 SIGINT의 기본 핸들러를 복원하기 위해 다른 것을 사용해야합니다.

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

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


2

프로세스를 죽이고 프로세스가 끝날 때까지 기다리 길 원하는 사람들에게는 :

신호 유형 당 최대 60 초 동안 대기합니다.

경고 :이 답변은 킬 신호를 포착하여 발송하는 것과 관련이 없습니다.

# close_app_sub GREP_STATEMENT SIGNAL DURATION_SEC
# GREP_STATEMENT must not match itself!
close_app_sub() {
    APP_PID=$(ps -x | grep "$1" | grep -oP '^\s*\K[0-9]+' --color=never)
    if [ ! -z "$APP_PID" ]; then
        echo "App is open. Trying to close app (SIGNAL $2). Max $3sec."
        kill $2 "$APP_PID"
        WAIT_LOOP=0
        while ps -p "$APP_PID" > /dev/null 2>&1; do
            sleep 1
            WAIT_LOOP=$((WAIT_LOOP+1))
            if [ "$WAIT_LOOP" = "$3" ]; then
                break
            fi
        done
    fi
    APP_PID=$(ps -x | grep "$1" | grep -oP '^\s*\K[0-9]+' --color=never)
    if [ -z "$APP_PID" ]; then return 0; else return "$APP_PID"; fi
}

close_app() {
    close_app_sub "$1" "-HUP" "60"
    close_app_sub "$1" "-TERM" "60"
    close_app_sub "$1" "-SIGINT" "60"
    close_app_sub "$1" "-KILL" "60"
    return $?
}

close_app "[f]irefox"

이름이나 인수로 죽일 앱을 선택합니다. grep 자체와 일치하지 않도록 앱 이름의 첫 글자를 괄호로 묶습니다.

약간의 변경으로, 명령문 pidof process_name대신 PID 또는 더 간단한 것을 직접 사용할 수 있습니다 ps.

코드 정보 : 마지막 grep은 후행 공백없이 PID를 얻는 것입니다.


1

원하는 배경 프로세스를 관리하는 것이 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

0

그래서 나는 이것으로도 고민했다. Bash에서는 함수를 정의하고 멋진 기능을 수행 할 수 있습니다. 내 동료와 나는을 사용 terminator하므로 배치 실행의 경우 출력을보고 싶다면 일반적으로 터미네이터 창을 모두 생성합니다 ( tmux탭 보다 우아하지 않지만 GUI와 유사한 인터페이스 haha를 사용할 수 있음).

여기에 내가 만든 설정과 실행할 수있는 물건의 예가 있습니다.

#!/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을 편집 하고 방금 제안을 보았으므로 훨씬 간단합니다! 감사!

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