Ctrl+C를 사용하여 bash 스크립트를 중지할 수 없습니다.

Ctrl+C를 사용하여 bash 스크립트를 중지할 수 없습니다.

나는 원격 시스템에 날짜와 핑을 인쇄하는 루프가 포함된 간단한 bash 스크립트를 작성했습니다.

#!/bin/bash
while true; do
    #     *** DATE: Thu Sep 17 10:17:50 CEST 2015  ***
    echo -e "\n*** DATE:" `date` " ***";
    echo "********************************************"
    ping -c5 $1;
done

터미널에서 실행하면 Ctrl+C. ^C터미널로 보낼 것 같지만 스크립트는 멈추지 않습니다.

MacAir:~ tomas$ ping-tester.bash www.google.com

*** DATE: Thu Sep 17 23:58:42 CEST 2015  ***
********************************************
PING www.google.com (216.58.211.228): 56 data bytes
64 bytes from 216.58.211.228: icmp_seq=0 ttl=55 time=39.195 ms
64 bytes from 216.58.211.228: icmp_seq=1 ttl=55 time=37.759 ms
^C                                                          <= That is Ctrl+C press
--- www.google.com ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 40.887/59.699/78.510/18.812 ms

*** DATE: Thu Sep 17 23:58:48 CEST 2015  ***
********************************************
PING www.google.com (216.58.211.196): 56 data bytes
64 bytes from 216.58.211.196: icmp_seq=0 ttl=55 time=37.460 ms
64 bytes from 216.58.211.196: icmp_seq=1 ttl=55 time=37.371 ms

몇 번을 누르든, 얼마나 빨리 누르든 상관없습니다. 나는 그것을 멈출 수 없다.
직접 테스트하고 구현해 보세요.

Ctrl+Z나는 그것을 중지 하고 중지하는 부가적인 솔루션으로 사용했습니다 kill %1.

여기서 무슨 일이 일어나고 있는 걸까요 ^C?

답변1

무슨 일이 일어나는지는 SIGINT bashpingbash아니요대화형, 둘 다 스크립트를 실행하는 대화형 쉘에 의해 생성되고 터미널의 전경 프로세스 그룹으로 설정된 동일한 프로세스 그룹 ping에서 실행됩니다.bash

그러나 bash해당 SIGINT는 현재 실행 중인 명령이 종료된 후에만 비동기적으로 처리됩니다. bash현재 실행 중인 명령이 SIGINT에 의해 종료된 경우(즉, 종료 상태가 SIGINT에 의해 종료되었음을 나타냄) SIGINT가 수신될 때만 종료됩니다.

$ bash -c 'sh -c "trap exit\ 0 INT; sleep 10; :"; echo here'
^Chere

위에서 bashCtrl-C를 누르면 SIGINT가 수신되지만 sh정상적으로 0 종료 코드로 종료되므로 SIGINT가 무시되므로 "여기"가 표시됩니다.sleepshbash

ping, 적어도 iputils가 작동하는 방식입니다. 중단되면 통계를 인쇄하고 핑 응답 여부에 따라 종료 상태 0 또는 1로 종료됩니다. 따라서 ping실행 중에 Ctrl-C를 누르면 SIGINT 핸들러를 bash눌렀 지만 정상적으로 종료되기 때문에 종료되지 않는 것을 알 수 있습니다 .Ctrl-Cpingbash

sleep 1해당 루프에 하나를 추가하고 실행 중인 Ctrl-C동안 누르면 SIGINT에 특별한 핸들러가 없기 때문에 SIGINT로 죽고 SIGINT로 죽었다고 보고합니다. 이 경우 종료됩니다(실제로 SIGINT로 자체 종료됩니다. 순서대로) 상위 항목에 인터럽트를 보고합니다.)sleepsleepbashbash

이 동작이 발생하는 이유는 bash확실하지 않으며 이 동작이 항상 결정적이지는 않다는 것을 알았습니다. 그냥 물어 봤어bash개발 메일링 리스트에 대한 질문(고쳐 쓰다: @Jilles가 이제 이유를 확인했습니다.그의 대답).

유사한 동작을 발견한 유일한 다른 쉘은 ksh93(업데이트,@Jilles가 언급했듯이 FreeBSD에서도 마찬가지입니다.sh). 거기에서 SIGINT는 눈에 띄게 무시되는 것 같습니다. ksh93명령이 SIGINT에 의해 종료될 때마다 종료됩니다.

위와 동일한 동작을 수행 bash하지만 다음과 같은 결과도 나타납니다.

ksh -c 'sh -c "kill -INT \$\$"; echo test'

"테스트"가 출력되지 않습니다. 즉, 대기 중인 명령이 SIGINT로 종료되면 해당 SIGINT 자체를 수신하지 못하더라도 SIGINT로 자체 종료하여 종료됩니다.

해결책은 다음을 추가하는 것입니다.

trap 'exit 130' INT

스크립트 상단에서 bashSIGINT가 수신되면 강제로 종료합니다. SIGINT는 어떤 경우에도 동기적으로 처리되지 않고 현재 실행 중인 명령이 종료된 후에만 처리됩니다.

이상적으로는 SIGINT에서 사망했음을 부모에게 보고하고 싶습니다. 따라서 다른 bash스크립트인 경우 해당 bash스크립트도 중단됩니다. an을 실행하는 것은 exit 130SIGINT에서 죽는 것과 같지는 않지만(일부 쉘은 $?두 경우 모두 동일한 값을 설정하지만) SIGINT가 죽는 것을 보고하는 데 종종 사용됩니다(SIGINT가 2인 시스템에서는 이것이 가장 많습니다).

그러나 FreeBSD bashksh93FreeBSD 에서는 sh이것이 작동하지 않습니다. 130 종료 상태는 SIGINT에 의해 종료로 처리되지 않으며 상위 스크립트는아니요거기 멈춰라.

따라서 아마도 더 나은 옵션은 SIGINT를 받은 후 SIGINT로 자살하는 것입니다.

trap '
  trap - INT # restore default INT handler
  kill -s INT "$$"
' INT

답변2

설명은 bash가 SIGINT 및 SIGQUIT에 대해 WCE(대기 및 협력 종료)를 구현한다는 것입니다.http://www.cons.org/cracauer/sigint.html. 이는 프로세스가 종료되기를 기다리는 동안 bash가 SIGINT 또는 SIGQUIT를 수신하면 프로세스가 종료될 때까지 기다리고 프로세스가 해당 신호에 따라 종료되면 자체적으로 종료된다는 것을 의미합니다. 이렇게 하면 사용자 인터페이스에서 SIGINT 또는 SIGQUIT를 사용하는 프로그램이 예상대로 작동합니다(신호로 인해 프로그램이 종료되지 않으면 스크립트가 정상적으로 계속됩니다).

SIGINT 또는 SIGQUIT를 포착한 후 결과적으로 종료되지만 신호를 자체적으로 다시 보내는 대신 일반 종료()를 사용하는 프로그램에서는 단점이 발생합니다. 그러한 프로그램을 호출하는 스크립트는 중단될 수 없습니다. 진정한 해결책은 ping이나 ping6과 같은 프로그램이라고 생각합니다.

유사한 동작이 ksh93 및 FreeBSD의 /bin/sh에서 구현되지만 대부분의 다른 쉘에서는 구현되지 않습니다.

답변3

터미널은 control-c를 인지하고 새로운 포그라운드 프로세스 그룹이 아직 생성되지 않았기 INT때문에 포그라운드 프로세스 그룹(이 경우 쉘 포함)에 신호를 보냅니다 . ping이는 트래핑을 통해 쉽게 확인할 수 있습니다 INT.

#! /bin/bash
trap 'echo oh, I am slain; exit' INT
while true; do
  ping -c5 127.0.0.1
done

실행 중인 명령이 새로운 포그라운드 프로세스 그룹을 생성하는 경우 control-c는 셸 대신 해당 프로세스 그룹으로 이동합니다. 이 경우 터미널이 신호를 내보내지 않으므로 쉘은 종료 코드를 확인해야 합니다.

( INT그런데, 쉘에서 이 작업을 수행하는 것은 매우 복잡할 수 있습니다. 쉘이 신호를 무시해야 할 때도 있고 그렇지 않을 때도 있기 때문입니다. 궁금하다면 소스 코드를 파헤치거나 생각해 보세요 tail -f /etc/passwd; echo foo. :)

답변4

글쎄, 나는 sleep 1bash 스크립트에 a를 추가하려고 시도했고 bam!
이제 2개로 멈출 수 있습니다 Ctrl+C.

를 누르면 현재 실행 중인 프로세스에 Ctrl+C신호 SIGINT가 전송되고 명령은 루프 내에서 실행됩니다. 그 다음에,서브쉘프로세스는 다른 프로세스를 시작하는 루프의 다음 명령으로 계속됩니다. 스크립트를 중지하려면 두 개의 SIGINT신호를 보내야 합니다. 하나는 현재 실행 중인 명령을 중단하고 다른 하나는 현재 실행 중인 명령을 중단합니다.서브쉘프로세스.

가 호출되지 않은 스크립트에서는 매우 빠르게 여러 번 sleep누르는 것이 Ctrl+C작동하지 않는 것 같으며 루프를 종료하는 것도 불가능합니다. 내 생각에는 두 번 누르는 것이 현재 실행 중인 프로세스가 중단되고 다음 프로세스가 시작되는 사이의 정확한 순간에 놓을 만큼 빠르지 않은 것 같습니다. 누를 때마다 루프 내에서 실행 중인 프로세스에 Ctrl+Ca가 전송되지만SIGINT서브쉘.

with 스크립트에서 sleep 1이 호출은 첫 번째 Ctrl+C(first SIGINT)에 의해 중단되면 1초 동안 실행을 일시 중지합니다.서브쉘다음 명령을 실행하는 데 더 많은 시간이 걸립니다. 이제 두 번째 항목 Ctrl+C(두 번째 항목 SIGINT)은 다음으로 이동합니다.서브쉘, 스크립트 실행이 종료됩니다.

관련 정보