다음을 수행하는 bash 스크립트가 있다고 가정해 보겠습니다.
while :
do
foo
done
나는 이 스크립트를 콘솔에서 실행할 수 있고, foo를 두 번 실행하는 사이에 발생하는 한 언제든지 종료할 수 있기를 원합니다. 따라서 Ctrl+를 누르면 C(이것은 스크립트를 종료시키는 또 다른 동작일 수 있습니다. Ctrl+ C는 단지 예일 뿐입니다), foo를 실행한 후 사용 가능한 다음 지점에서 종료됩니다.
while :
do
foo
if [pressed_ctrl_c]:
break
done
답변1
다음 구성을 시도해 볼 수 있습니다.
#!/bin/bash
#
INTR=
trap 'INTR=yes; echo "** INTR **" >&2' INT
while :
do
(
# Protect the subshell block
trap '' INT
# Protected code here
echo -n "The date/time is: "
sleep 2
date
read -t2 -p 'Continue (y/n)? ' YN || echo
test n = "$YN" && echo "Asked for BREAK" >&2 && exit 90
)
SS=$?
test 90 -eq $SS && echo "Matched BREAK" >&2 && break
# Ctrl/C, perhaps?
test yes = "$INTR" && echo "Matched INTR" >&2 && break
done
exit 0
몇 가지 메모
read
쌍은test
블록 내에서 보호된 코드 세그먼트의 대화형 제어를 보여줍니다( ... )
.- 이는 서브쉘 내부
exit 90
와 동일하지만 내부에서 발생합니다 .break
서브쉘 블록의 끝 바로 뒤에 있는 줄은test 0 != $? ...
상태를 캡처exit 90
하고break
코드가 실제로 원하는 것을 구현하는 데 사용됩니다. - 서브셸은 다양한 종료 상태 값을 사용하여 다양한 유형의 원하는 제어 흐름( 등...)
break
을 나타낼 수 있습니다.exit
- 이는 프로그램이 자체 신호 처리기를 설치하는 것을 막지는 않습니다. 예를 들어
gdb
( )에 대한 자체 처리기를 설치합니다. 사용자가 세션을 중단하는 것을 방지하는 것이 목표인 경우 인터럽트 키를 변경하면 상황을 난독화하는 데 도움이 될 수 있습니다(아래 코드 참조). 우아하지는 않지만 아마도 효과적일 것입니다.SIGINT
CtrlC
터미널에서 SIGINT 키 변경
G=$(stty -g) # Save settings
test -n "$G" && stty intr ^A # That is caret and A, not Ctrl/A
# ... SIGINT generated with Ctrl/A rather than Ctrl/C ...
test -n "$G" && stty "$G" # Restore original settings
답변2
이것은 작동하는 것 같습니다:
#!/bin/sh
pressed_ctrl_c=
trap "pressed_ctrl_c=1" INT
while true
do
(trap "" INT; foo)&
wait || wait
if [ "$pressed_ctrl_c" ]
then
# echo
break
fi
done
pressed_ctrl_c
비어 있도록 초기화되었습니다 . 이는 스크립트에서 필요하지 않을 수도 있습니다.trap command signum
캡처 신호 번호를 설정하도록 쉘에 지시signum
그리고 실행command
하나를 캡처할 때. "INT"는 Ctrl신호 + 생성에 대한 기술 용어 인 "인터럽트 신호"의 약어인 "SIGINT"의 약어 이므로 + 를 C입력하면 쉘이 이를 1로 설정합니다. (SIGINT의 값은 2이므로 타이핑 시간을 절약하고 싶다면 이렇게 말하면 됩니다.)CtrlCpressed_ctrl_c
trap "pressed_ctrl_c=1" 2
- 루프 내부에는 괄호 안에 명령줄이 있습니다. 그러면 서브쉘이 생성됩니다.
- 이 명령을 다시 사용해 보겠습니다
trap
. 이 시간command
빈 문자열입니다. 이는 쉘이 신호 번호를 무시하도록 지시합니다.signum
. 괄호 안에 있으므로 하위 쉘에만 영향을 미칩니다. - 달리기
foo
. 서브쉘에서 실행되기 때문에 +는foo
무시됩니다 . 즉, 입력해도 계속 실행됩니다.CtrlC - 서브쉘을 백그라운드에 두고...
- ...완료될 때까지 기다립니다.
- 명령이 성공 하면
wait
명령문 실행이 계속됩니다if
. 실패하면 다시 하세요. (나는 이것에 대해 다시 돌아올 것이다.) - 설정된 경우
$pressed_ctrl_c
루프를 중단합니다.echo
+가 터미널에 표시되고 Ctrl다음 줄로 이동하려는 경우 명령의 주석 처리를 제거하도록 선택할 수 있습니다.C^C
백그라운드에서 명령을 실행한 다음 wait
즉시 실행합니다. 이는 포그라운드에서 명령을 실행하는 것과 매우 유사합니다(적어도 스크립트 내에서 실행하는 경우). wait
명령은 서브쉘이 종료될 때, 즉 foo
명령이 종료될 때 성공적으로 종료됩니다. ( 오류가 반환 wait
되어도 명령은 성공적으로 종료됩니다.) 첫 번째 명령이 성공적으로 종료되면 두 번째 명령을 건너뛰고 으로 이동합니다 .foo
wait
if
루프를 실행하는 셸은 인터럽트를 포착하지만 하위 셸과 프로세스는 foo
이를 무시합니다. 따라서 Ctrl+ 를 입력하면 C쉘이 pressed_ctrl_c
1로 설정되고 (첫 번째) wait
명령이 중단됩니다. 첫 번째 wait
명령이 실패했으므로 두 번째 명령을 계속 진행합니다. foo
서브셸은 여전히 실행 중이므로 아직 수행할 작업이 있다는 점을 기억하세요 wait
(즉, 완료될 때까지 기다립니다 foo
).
마지막으로, 이 변수가 실행 중에 +를 눌렀음을 나타내도록 설정된 경우 Ctrl출력 루프를 중단합니다.Cfoo
Ctrl+를 두 번 누르면 C두 번째 명령이 중단되고 두 번째 wait
명령이 중단됩니다. 이렇게 하면 스크립트가 종료되고 foo
백그라운드에서 실행되는 동안 쉘 프롬프트로 돌아갑니다. wait || wait || wait || wait
원하는 만큼 늘려서 이 문제를 완화할 수 있습니다 . Ctrl스크립트를 조기에 종료하려면 +를 각각 한 번씩 입력해야 합니다 .Cwait
오!
이에 대한 한 가지 문제는 스크립트에 의해 백그라운드에 배치된 프로세스의 표준 입력이 로 설정되어 있다는 것입니다 /dev/null
. foo
키보드에서 읽는 경우 위의 내용을 수정해야 합니다.