SIGINT 등을 잡을 때 종료 코드를 유지 하시겠습니까?


14

http://linuxcommand.org/wss0160.php#trap 에서 trap설명한 것과 같이 사용 하여 종료하기 전에 ctrl-c (또는 유사한) 및 정리를 잡으면 종료 코드가 변경됩니다.

이제 이것은 아마도 실제 세계에서 차이를 만들지 않을 것입니다 (예 : 종료 코드가 이식 가능하지 않고 프로세스가 종료 될 때 기본 종료 코드 에서 논의 된 것처럼 항상 명확하지는 않기 때문에 ) ? 실제로이를 방지하고 대신 중단 된 스크립트에 대한 기본 오류 코드를 반환 할 방법이 없습니까?

예 (bash에서 내 질문은 bash 특정으로 간주되어서는 안됩니다) :

#!/bin/bash
trap 'echo EXIT;' EXIT
read -p 'If you ctrl-c me now my return code will be the default for SIGTERM. ' _
trap 'echo SIGINT; exit 1;' INT
read -p 'If you ctrl-c me now my return code will be 1. ' _

산출:

$ ./test.sh # doing ctrl-c for 1st read
If you ctrl-c me now my return code will be the default for SIGTERM.
$ echo $?
130
$ ./test.sh # doing ctrl-c for 2nd read
If you ctrl-c me now my return code will be the default for SIGTERM.
If you ctrl-c me now my return code will be 1. SIGINT 
EXIT
$ echo $?
1

(POSIX 준수를 위해 제거하도록 편집되었습니다.)

(대신 bash 스크립트로 다시 편집하면 내 질문은 쉘별로 다릅니다.)

휴대 할 수없는 "SIGINT"를 위해 휴대용 "INT"를 트랩에 사용하도록 편집했습니다.

쓸모없는 중괄호를 제거하고 잠재적 솔루션을 추가하도록 편집되었습니다.

최신 정보:

하드 코드 된 오류 코드로 종료하고 EXIT를 트랩하여 간단하게 해결했습니다. 오류 코드가 다르거 나 EXIT 트랩을 사용할 수 없지만 내 경우에는 충분하기 때문에 특정 시스템에서 문제가 될 수 있습니다.

trap cleanup EXIT
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM

스크립트가 약간 이상하게 보입니다. read현재 코 프로세스에서 읽 도록 지시 trap cmd SIGINT하고 표준에서 사용해야한다고 말하는 것처럼 작동하지 않습니다 trap cmd INT.
— schily

예, POSIX에서는 SIG 접두사가 없습니다.
— phk

죄송하지만 "read -p"도 지원되지 않으므로 bash에 맞게 조정하겠습니다.
— phk

@schily : "coprocess"의 의미를 모르겠습니다.
— phk

Korn Shell 매뉴얼 페이지 read -p에는 현재 코 프로세스의 입력을 읽습니다.
— schily

답변:


4

정리 처리기 내 에서 EXIT 처리기를 변경하기 만하면 됩니다. 예를 들면 다음과 같습니다.

#!/bin/bash
cleanup() {
    echo trapped exit
    trap 'exit 0' EXIT
}
trap cleanup EXIT
read -p 'If you ctrl-c me now my return code will be the default for SIGTERM. '

trap cleanup INT대신에 의미 했 trap cleanup EXIT습니까?
— Jeff Schaller

나는 EXIT을 의미한다고 생각합니다. exit가 마지막으로 호출되었지만 완료되면 반환 0으로 종료하도록 트랩을 변경합니다. 신호 처리기가 재귀 적이라고 생각하지 않습니다.
— rocky

좋아, 귀하의 예에서 나는 사용자가 ctrl-c를 통해 대화 형 스크립트를 종료하더라도 실제로 정리하기 위해 (SIG) INT를 전혀 포획하지 않습니다.
— phk

@phk Ok. 나는 당신이 exit를 강제로 0으로 반환하는 방법에 대한 아이디어를 얻었지만 내가 모은 것이 문제라고 생각합니다. 트랩 EXIT 설정을 SIGINT가 호출하는 실제 정리 처리기 안에 넣도록 코드를 조정할 수 있다고 가정합니다.
— rocky

@ rocky : 내 목표는 일반적으로 처리기가없는 종료 코드를 변경하지 않고 트랩 처리기를 갖는 것이 었습니다. 이제는 일반적으로 얻을 수있는 종료 코드를 반환 할 수는 있지만 문제는이 경우 정확한 종료 코드를 알지 못한다는 것입니다 (연결 된 스레드와 meuh의 게시물에서 볼 수 있듯이 매우 복잡합니다). 귀하의 게시물이 여전히 도움이되었지만 트랩 핸들러를 동적으로 재정의하는 것에 대해서는 생각하지 않았습니다.
— phk

9

실제로 bash의 내부 인터럽트는 bash가 read실행하는 명령을 인터럽트하는 것과 약간 다릅니다. 일반적으로을 입력 trap하면 $?이 설정되어 있으며 동일한 값으로 유지하고 종료 할 수 있습니다.

trap 'rc=$?; echo $rc SIGINT; exit $rc' INT
trap 'rc=$?; echo $rc EXIT; exit $rc' EXIT

같은 명령을 실행할 때 스크립트가 중단 된 경우 sleep 또는 같은 심지어 내장을 wait, 당신은 볼 것이다

130 SIGINT
130 EXIT

그리고 종료 코드를 위해, 그러나 130 인 read -p것 같다, $?(어쨌든 bash는 4.3.42의 내 버전) 0입니다.


read내 릴리스의 변경 파일에 따라 신호 처리 중일 수 있습니다 ... (/ usr / share / doc / bash / CHANGES)

이 버전 bash-4.3-alpha와 이전 버전 bash-4.2-release 사이의 변경 사항.

  1. 배쉬의 새로운 기능

    아르 자형. Posix 모드에있을 때 트랩 된 신호에 의해 '판독'이 중단 될 수 있습니다. 트랩 처리기를 실행 한 후 읽기는 128+ 신호를 반환하고 부분적으로 읽은 입력을 버립니다.


네, 정말 이상합니다. bash 4.3.42 (cygwin 아래)의 대화 형 쉘에서는 130이며 스크립트 및 POSIX 모드에있을 때 같은 쉘 아래에 0이 있거나 차이가 없습니다. 그러나 대시와 비지 박스에서는 항상 1입니다.
— phk

나는 종료되지 않는 트랩으로 POSIX 모드를 시도하고 readCHANGES 파일 (내 답변에 추가됨)에 명시된 것처럼를 다시 시작합니다 . 따라서 진행중인 작업 일 수 있습니다.
— meuh

종료 코드 130은 100 % 이식 불가능합니다. Bourne Shell은 128 + signo신호의 종료 코드로 사용하지만 ksh93은을 사용합니다 256 + signo. POSIX의 말 : 128 이상 ....
— schily

@schily 원래 게시물에 링크 된 스레드 ( unix.stackexchange.com/questions/99112 )에 명시된 바와 같이 True 입니다.
— phk

6

$?트랩 핸들러에 들어가면 일반적인 신호 종료 코드를 사용할 수 있습니다 .

sig_handler() {
    exit_status=$?  # Eg 130 for SIGINT, 128 + (2 == SIGINT)
    echo "Doing signal-specific up"
    exit "$exit_status"
}
trap sig_handler INT HUP TERM QUIT

별도의 EXIT 트랩이있는 경우 동일한 방법을 사용할 수 있습니다. 신호 처리기에서 전달 된 종료 상태 (있는 경우)를 즉시 정리 한 다음 저장된 종료 상태를 리턴하십시오.


이것은 대부분의 껍질에 적용됩니까? 아주 좋아요 localPOSIX는 아닙니다.
— phk

1
편집했습니다. 이외의 테스트는하지 않았습니다 {ba,z}sh. AFAIK, 그것은 상관없이 할 수있는 최선입니다.
— Tom Hale

이것은 대시에서 작동하지 않습니다 ( $?수신 된 신호가 무엇이든 1입니다).
— MoonSweep

2

SIGINT에 의한 이탈을 시뮬레이션하기에 오류 코드로는 충분하지 않습니다. 지금까지 아무도 이것을 언급하지 않은 것에 놀랐습니다. 추가 자료 : https://www.cons.org/cracauer/sigint.html

올바른 방법은 다음과 같습니다

for sig in EXIT ABRT HUP INT PIPE QUIT TERM; do
    trap "cleanup;
          [ $sig  = EXIT ] && normal_exit_only_cleanup;
          [ $sig != EXIT ] && trap - $sig EXIT && kill -s $sig $$
         " $sig
done

이것은 Bash, Dash 및 zsh와 함께 작동합니다. 추가 이식성을 위해서는 숫자 신호 사양을 사용해야합니다 (반면에 zsh는 kill명령에 문자열 매개 변수가 필요합니다 ).

또한 EXIT신호 의 특수 처리에 유의하십시오 . 이것은 일부 쉘 (즉, Bash) EXIT이 모든 신호 에 대해 트랩을 실행하기 때문입니다 (해당 신호에 트랩을 정의한 후). EXIT트랩을 리셋 하면 방지 할 수 있습니다.

"정상"종료에서만 코드 실행

이 검사를 [ $sig = EXIT ]통해 정상적인 (신호가없는) 종료에서만 코드를 실행할 수 있습니다. 그러나 모든 신호에는 트랩이 있어야 트랩을 재설정 할 수 있습니다 EXIT. normal_exit_only_cleanup그렇지 않은 신호에 대해서도 호출됩니다. 또한를 통해 이탈로 실행됩니다 set -e. 이것은 ERR(Dash에서 지원하지 않는) 트 랩핑 하고에 확인 표시 [ $sig = ERR ]를 추가하여 해결할 수 있습니다 kill.

단순화 된 배쉬 전용 버전

반면에,이 동작은 Bash에서 간단히 할 수 있음을 의미합니다

trap cleanup EXIT

정리 코드를 실행하고 종료 상태를 유지합니다.

편집

  • "EXIT가 모든 것을 포착"하는 정교한 Bash의 동작

  • 갇힐 수없는 KILL 신호 제거

  • 신호 이름에서 SIG 접두사 제거

  • 시도하지 마십시오 kill -s EXIT

  • 취 set -e/ ERR을 고려


신호를 포착하는 프로그램이 신호를 수신했다는 사실을 보존하는 방식으로 종료해야한다는 일반적인 요구 사항은 없습니다. 예를 들어, 프로그램은 전송 SIGINT이 종료하는 방법 이라고 선언 할 수 있으며 오류없이 종료 된 경우 0으로 종료하거나 완전히 종료되지 않은 경우 다른 오류 코드로 종료하기로 결정할 수 있습니다. 적절한 사례 : top. 실행 top; echo $?한 다음 Ctrl-C를 누르십시오. 화면에 덤프 된 상태는 0입니다.
— Louis

1
지적 해 주셔서 감사합니다. 그러나 포스터는 구체적으로 종료 코드 유지 에 대해 물었습니다 .
— philipp2100
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.