태그 보관물: trap

trap

`set -eu`를 사용할 때 EXIT 및 ERR 트랩의 올바른 동작 errexit), set

ERR 및 EXIT 트랩과 함께 set -e( errexit), set -u( nounset)를 사용할 때 이상한 동작이 관찰됩니다 . 그것들은 관련이있는 것처럼 보이므로, 하나의 질문에 넣는 것이 합리적입니다.

1) set -uERR 트랩을 트리거하지 않습니다

  • 암호:

    #!/bin/bash
    trap 'echo "ERR (rc: $?)"' ERR
    set -u
    echo ${UNSET_VAR}
  • 예상 : ERR 트랩이 호출됩니다. RC! = 0
  • 실제 : ERR 트랩이 호출 되지 않음 , RC == 1
  • 참고 : set -e결과를 변경하지 않습니다

2) set -euEXIT 트랩에서 종료 코드 사용은 1 대신 0입니다.

  • 암호:

    #!/bin/bash
    trap 'echo "EXIT (rc: $?)"' EXIT
    set -eu
    echo ${UNSET_VAR}
  • 예상 : EXIT 트랩이 호출되고 RC == 1
  • 실제 : EXIT 트랩 호출, RC == 0
  • 참고 :를 사용할 때 set +eRC == 1입니다. EXIT 트랩은 다른 명령에서 오류가 발생하면 올바른 RC를 반환합니다.
  • 편집 : 이 주제 에 사용되는 Bash 버전과 관련이 있다는 흥미로운 의견 이있는 SO 게시물 이 있습니다. Bash 4.3.11로이 스 니펫을 테스트하면 RC = 1이되므로 더 좋습니다. 불행히도 현재 모든 호스트에서 Bash (3.2.51에서)를 업그레이드하는 것은 불가능하므로 다른 해결책을 찾아야합니다.

누구든지 이러한 행동 중 하나를 설명 할 수 있습니까?

이러한 주제를 검색하는 데 성공하지 못했습니다. Bash 설정 및 트랩에 대한 게시물 수가 많으면 놀랍습니다. 그러나 포럼 스레드하나 있지만 결론은 다소 불만족입니다.



답변

보낸 사람 man bash:

  • set -u
    • 취급 해제 변수 특별한 변수 이외의 변수 "@""*"파라미터 확장을 수행 할 때 에러 등. 설정되지 않은 변수 또는 매개 변수에서 확장을 시도하면, 쉘은 오류 메시지를 인쇄하고, -i활성화 되지 않은 경우 0이 아닌 상태로 종료됩니다.

POSIX에 따르면 확장 오류가 발생 하면 확장이 쉘 특수 내장 ( 어쨌든 정기적으로 무시되므로 관계가 없음) 또는 기타 유틸리티 와 연관 될 때 비 대화식 쉘 이 종료 됩니다 .bash

  • 쉘 오류의 결과 :
    • 확장 오류Word Expansions에 정의 된 셸 확장 이 수행 될 때 발생 하는 오류 입니다 (예 : 유효한 연산자가 아니기 "${x!y}"때문에 !) . 구현은 수도 가 아닌 확장하는 동안, 토큰 화하는 동안이를 감지 할 수있는 경우 구문 오류 이러한 치료.
    • [A] n 대화식 쉘은 종료하지 않고 표준 오류에 진단 메시지를 작성해야합니다.

또한 man bash:

  • trap ... ERR
    • sigspec이 ERR 인 경우 다음 조건에 따라 파이프 라인 (단일 단순 명령으로 구성 될 수 있음) , 목록 또는 복합 명령이 0이 아닌 종료 상태를 리턴 할 때마다 명령 arg 가 실행 됩니다 .
      • ERR의 실패한 명령은 즉시 다음 명령 목록의 일부인 경우 트랩은 실행되지 않습니다 while또는 until키워드가 …
      • if성명서 에서 테스트의 일부 …
      • … 최종 또는 다음에 오는 명령을 제외하고 &&또는 ||목록 에서 실행 된 명령의 일부 …&&||
      • … 파이프 라인의 명령이지만 마지막 명령은 …
      • … 또는를 사용하여 명령의 반환 값이 반전되는 경우 !.
    • 이 옵션 은 errexit -e 옵션 과 동일한 조건 입니다.

것을 위의 참고 ERR의 트랩이 일부의 평가에 대한 모든 것입니다 다른 명령의 반환. 그러나 확장 오류 가 발생하면 아무 것도 반환하기위한 명령이 실행되지 않습니다. 당신의 예에서, echo 일이 결코 – 쉘 평가하고 그 인수를 확대하면서이 발생하기 때문에 -u현재, 스크립트 쉘에서 즉시 종료의 원인이 명시 적 쉘 옵션으로 지정되어 NSET 변수를.

따라서 EXIT 트랩 (있는 경우)이 실행되고 셸은 진단 메시지와 함께 종료되고 0이 아닌 종료 상태가 그대로 있어야합니다.

에 관해서는 RC : 0 건, 그 어떤 종류의 버전 특정 버그입니다 기대 – 아마도에 대한 두 개의 트리거와 함께 할 EXIT 동시에 발생하고 상대방의 종료 코드를 얻는 일 (발생하지 않습니다) . 어쨌든, 최신 bash바이너리는 다음에 의해 설치됩니다 pacman.

bash <<\IN
    printf "shell options:\t$-\n"
    trap 'echo "EXIT (rc: $?)"' EXIT
    set -eu
    echo ${UNSET_VAR}
IN

첫 번째 줄을 추가하여 셸의 조건이 스크립팅 된 셸의 조건임을 알 수 있습니다 . 대화식 이 아닙니다 . 출력은 다음과 같습니다.

shell options:  hB
bash: line 4: UNSET_VAR: unbound variable
EXIT (rc: 1)

다음은 최근 변경 로그의 관련 참고 사항입니다 .

  • 비동기 명령이 $?올바르게 설정되지 않던 문제를 수정했습니다 .
  • 명령의 확장 오류
    생성 된 오류 메시지 for가 잘못된 행 번호를 갖도록 하는 버그를 수정했습니다 .
  • 비동기 서브 쉘 명령에서 SIGINTSIGQUIT 를 사용할 수없는 버그가 수정 trap되었습니다.
  • 대화식 쉘 에서 두 번째 이후의 SIGINT 가 무시되는 인터럽트 처리 문제를 수정했습니다 .
  • 쉘은 더 이상 trap해당 신호에 대한 핸들러를 실행하는 동안 신호 수신을 차단하지 않으며 대부분의 trap 핸들러가 재귀 적으로 실행되도록합니다
    ( 핸들러가 실행 trap되는 동안 trap핸들러 실행) .

가장 관련성이 높은 마지막 또는 첫 번째 또는 두 가지의 조합이라고 생각합니다. trap핸들러는 매우 자연입니다 비동기 의 전체 작업이 대기하고 처리 할 수 있기 때문에 비동기 신호를 . 그리고 당신은 동시에 두 개의 트리거 -eu하고 $UNSET_VAR.

따라서 업데이트해야 할 수도 있지만 원하는 경우 다른 셸을 사용하여 업데이트해야합니다.


답변

(Bash 4.2.53을 사용하고 있습니다). 1 부에서 bash 맨 페이지는 “오류 메시지가 표준 오류에 기록되고 비 대화식 쉘이 종료됩니다”라고 표시합니다. ERR 트랩이 호출 될 것이라고 말하지는 않지만 그것이 가능하다면 유용 할 것이라는 데 동의합니다.

실용적으로 정의되지 않은 변수에 더 명확하게 대처하는 것이 가능한 경우 가능한 해결책은 대부분의 코드를 함수 안에 넣은 다음 해당 함수를 서브 쉘에서 실행하고 리턴 코드와 stderr 출력을 복구하는 것입니다. 다음은 “cmd ()”가 함수 인 예입니다.

#!/bin/bash
trap 'rc=$?; echo "ERR at line ${LINENO} (rc: $rc)"; exit $rc' ERR
trap 'rc=$?; echo "EXIT (rc: $rc)"; exit $rc' EXIT
set -u
set -E # export trap to functions

cmd(){
 echo "args=$*"
 echo ${UNSET_VAR}
 echo hello
}
oops(){
 rc=$?
 echo "$@"
 return $rc # provoke ERR trap
}

exec 3>&1 # copy stdin to use in $()
if output=$(cmd "$@" 2>&1 >&3) # collect stderr, not stdout 
then    echo ok
else    oops "fail: $output"
fi

내 bash에서 나는 얻는다.

./script my stuff; echo "exit was $?"
args=my stuff
fail: ./script: line 9: UNSET_VAR: unbound variable
ERR at line 15 (rc: 1)
EXIT (rc: 1)
exit was 1


답변