프로세스가 종료되었음을 알리는 대신 "trap"이 0을 전달하는 이유는 무엇입니까?

프로세스가 종료되었음을 알리는 대신 "trap"이 0을 전달하는 이유는 무엇입니까?

다음을 고려하세요:

#!/bin/bash

trap 'echo $?' INT
kill -INT $$

산출:0

여기서 나는 130내 시스템을 기대합니다. 물론 내가 a를 했다면 Ctrl + C나는 130.

HUP또는 같은 다른 신호에서도 마찬가지입니다 TERM. trap많은 신호를 포착하는 설정이 있는 경우 핸들러가 호출된 신호에 대한 올바른 오류 코드로 종료할 수 없기 때문에 이 동작이 놀랍습니다 .

#!/bin/dash

exit_abrupt() {
    exit_code=$?
    echo "Encountered an error, cleaning up..." >&2
    # *clean up*

    exit "$exit_code" # This will return 0!
}

trap exit_abrupt HUP INT TERM
kill -HUP $$

Bash, Dash 및 ZSH를 테스트했는데 모두 이러한 동작을 나타냈습니다. 이것이 크로스 쉘이 작동하는 방식입니까? POSIX에 의해 문서화되어 있습니까(누군가 나에게 문서를 알려줄 수 있습니까)? 셸에서 실행하기 위한 올바른 종료 코드를 어떻게 알 수 있나요?

최고POSIX 문서내가 읽은 내용은 다음과 같습니다.

"$"의 값은 무엇입니까? 트랩 작업의 완료는 트랩이 호출되기 전의 값이어야 합니다.

나에게는 모든 경우에 신호가 전달되어야 하는 것처럼 들리지만 그렇지 않으므로 뭔가 빠졌을 것입니다...

답변1

$?마지막 명령 실행 및 대기의 종료 상태가 포함됩니다. 다음에서 찾을 수 있습니다:

$ bash -c 'trap "echo \$?" INT; sleep 10; exit'
^C130

130은 ^C sleep에서 SIGINT를 수신 bash하고 bash가 반환 후 핸들러를 실행하고 명령 sleep의 종료 상태를 인쇄했기 때문에 보고되었습니다.sleep

존재하다:

$ bash -c 'trap "echo \$?" INT; kill -s INT "$$"; exit'
0

killSIGINT 핸들러를 호출하기 전에 bash가 마지막으로 실행한 명령인 명령의 종료 상태를 얻을 수 있습니다 .

$ bash -c 'trap "echo \$?" INT; (trap "" INT; sleep 3; exit 123); exit'
^C123

sleepSIGINT를 무시하면 Ctrl+를 누른 후에도 C실행 중인 서브셸이 반환될 때까지 기다려야 하며 sleep, 이때 핸들러는 서브셸의 종료 상태(123)를 인쇄합니다.

여러 신호에 대해 동일한 처리기를 설치하려면 신호를 처리기에 전달할 수 있습니다.

handler() {
  local signal="$1"
  echo "I got $signal signal"
}
for signal in INT HUP TERM QUIT; do
  trap "handler $signal" "$signal"
done

그건 그렇고, 당신은 찾을 수 있습니다:

bash -c '(trap "" INT; sleep 3; exit 123); exit'

Ctrl+ 로 중단 하려고 해도 c종료 상태 123으로 종료되는데, 이는 실제로 bash의 설계입니다. 자세한 내용은 다음을 방문하세요.터미널에 ctrl-c를 입력할 때 포그라운드 작업이 완료되기 전에 종료되지 않는 이유는 무엇입니까?

답변2

EXIT누군가가 필요할 경우를 대비하여 항상 올바른 종료 상태를 전달하면서 트랩을 치명적인 신호 트랩(키 입력을 통해 수신하든 쉘 프로세스로 전송된 신호이든)과 결합하기 위한 최종 솔루션은 다음과 같습니다.

#!/bin/sh

set -e

# https://unix.stackexchange.com/questions/752570/why-does-trap-passthough-zero-instead-of-the-signal-the-process-was-killed-wit
handle_exit() {
    exit_code=$?
    signal="$1"

    if [ "$exit_code" != 0 ] || [ "$signal" ]; then
        echo "\nProgram was exited abruptly!" >&2
    fi

    # Or do clean up here...

    if [ "$exit_code" != 0 ]; then
        trap -- - EXIT
        exit "$exit_code"
    elif [ "$signal" ]; then
        trap -- - "$signal"
        kill -s "$signal" -- $$
    fi
}

# POSIX sh doesn't include signals in its EXIT trap so list them ourselves
# SIG prefixes removed for POSIX sh compatibility
for signal in HUP INT TERM; do
    # shellcheck disable=SC2064
    trap "handle_exit $signal" "$signal"
done
trap handle_exit EXIT

# Your code starts here...

이는 리터럴 ( ) 을 트리거하면 트랩이 트랩보다 먼저 트리거되기 trap -- - EXIT때문에 필요합니다 . 이 특별한 경우(리터럴로 인해 ) 도 로 설정됩니다 . 따라서 핸들러가 두 번 트리거되는 것을 방지하기 위해 분기에서 트랩을 제거합니다 . 또한 스택 오버플로가 발생할 때까지 핸들러를 영원히 재귀적으로 호출하지 마십시오.INTCtrl + CINTEXITCtrl + C$exit_code130EXITiftrap -- - "$signal"

모든 쉘(Posh 포함)에서는 매력처럼 작동합니다.

관련 정보