명령 대체 오류를 캡처하려면 "-o errtrace"를 사용하십시오(예: set -E).

명령 대체 오류를 캡처하려면 "-o errtrace"를 사용하십시오(예: set -E).

이에 따르면참고 도서:

-E(-o errtrace라고도 함)

설정된 경우 ERR의 모든 트랩은 셸 함수, 명령 대체 및 하위 셸 환경에서 실행되는 명령에 의해 상속됩니다. 이 경우 ERR 트랩은 일반적으로 상속되지 않습니다.

그러나 다음이 작동하지 않기 때문에 잘못 해석한 것 같습니다.

#!/usr/bin/env bash
# -*- bash -*-

set -e -o pipefail -o errtrace -o functrace

function boom {
  echo "err status: $?"
  exit $?
}
trap boom ERR


echo $( made up name )
echo "  ! should not be reached ! "

나는 이미 간단한 할당이 my_var=$(made_up_name)스크립트를 종료한다는 것을 알고 있습니다 set -e(예: errexit).

-E/-o errtrace위의 코드처럼 작동해야 할까요? 아니면 제가 잘못 읽었던 걸까요?

답변1

참고: zsh여기 있는 대부분의 예제에서 "인라인 주석"을 허용하도록 구성하지 않고 에서 했던 것처럼 수행하지 마세요 sh <<-\CMD.

글쎄요, 위에 댓글에서 말했듯이, 저는 구체적으로 알지 못합니다.배쉬의set -E, 그러나 POSIX 호환 쉘이 (필요한 경우) 값을 테스트하는 쉬운 방법을 제공한다는 것을 알고 있습니다.

    sh -evx <<-\CMD
    _test() { echo $( ${empty:?error string} ) &&\
        echo "echo still works" 
    }
    _test && echo "_test doesnt fail"
    # END
    CMD
sh: line 1: empty: error string
+ echo

+ echo 'echo still works'
echo still works
+ echo '_test doesnt fail'
_test doesnt fail

위에는 내가 사용했지만parameter expansion테스트로 이동${empty?} _test()아직 return에스원패스 - 마지막에 표시된 대로echo이는 실패한 값이 종료되기 때문에 발생합니다.$( command substitution )하위 쉘은 포함하지만 상위 쉘은 포함하지 않습니다._test이 시점에서 계속 진행하십시오. 그리고echo상관하지 마세요 - 단 한 사람만으로도 행복해요\newline; echo아니요시험.

하지만 다음 사항을 고려해보세요.

    sh -evx <<-\CMD
    _test() { echo $( ${empty:?error string} ) &&\
            echo "echo still works" ; } 2<<-INIT
            ${empty?function doesnt run}
    INIT
    _test ||\
            echo "this doesnt even print"
    # END
    CMD
_test+ sh: line 1: empty: function doesnt run

왜냐면 내가 먹였으니까_test()'s입력의 사전 평가된 매개변수INIT here-document지금_test()함수가 전혀 실행을 시도하지도 않습니다. 또 뭔데sh껍질은 유령을 완전히 포기한 것 같습니다echo "this doesnt even print" 인쇄도 하지 않습니다.

아마 그럴 거예요아니요당신은 무엇을 원합니까?

이런 일이 일어나는 이유는${var?}스타일 매개변수 확장은나가는 것을 목표로 삼아shell매개변수가 누락된 경우이렇게 일해:

${parameter:?[word]}

다음과 같은 경우 오류가 표시됩니다.Null또는Unset.매개변수가 설정되지 않았거나 비어 있는 경우expansion of word(혹은 해당 단어가 생략된 경우, 설정되지 않았다는 메시지)를 표시해야 합니다.written to standard error그리고shell exits with a non-zero exit status.그렇지 않으면 값parameter shall be substituted.대화형 쉘은 종료할 필요가 없습니다.

문서 전체를 복사/붙여넣지는 않겠지만 실패를 원하신다면set but null다음 형식의 값을 사용합니다.

${var :? error message }

와 함께:colon위와 같이. 당신이 하나를 원한다면null성공하려면 콜론을 생략하세요. 나중에 설명하겠지만 값을 설정할 때만 이를 부정하고 실패할 수도 있습니다.

또 다른 실행_test():

    sh <<-\CMD
    _test() { echo $( ${empty:?error string} ) &&\
            echo "echo still works" ; } 2<<-INIT
            ${empty?function doesnt run}
    INIT
    echo "this runs" |\
        ( _test ; echo "this doesnt" ) ||\
            echo "now it prints"
    # END
    CMD
this runs
sh: line 1: empty: function doesnt run
now it prints

이는 다양한 빠른 테스트에 작동하지만 위에서 볼 수 있습니다._test(), 중앙에서 달려라pipeline실패합니다. 실제로는 다음을 포함합니다.command list함수 내부의 명령 중 어느 것도 실행되지 않고 다음 명령도 실행되지 않기 때문에 서브셸이 완전히 실패합니다.echo완벽하게 작동하지만 쉽게 테스트할 수 있는 것으로 나타났습니다.echo "now it prints" 지금 인쇄하세요.

저는 디테일이 성공과 실패를 결정한다고 생각합니다. 위의 예에서 기존 쉘은 다음과 같습니다.아니요스크립트된_main | logic | pipeline하지만( subshell in which we ${test?} ) ||그래서 약간의 샌드박싱이 필요합니다.

이는 명백하지 않을 수도 있지만 반대 사례를 전달하려는 경우 또는set=가치도 매우 간단합니다.

    sh <<-\CMD
    N= #N is NULL
    _test=$N #_test is also NULL and
    v="something you would rather do without"    
    ( #this subshell dies
        echo "v is ${v+set}: and its value is ${v:+not NULL}"
        echo "So this ${_test:-"\$_test:="} will equal ${_test:="$v"}"
        ${_test:+${N:?so you test for it with a little nesting}}
        echo "sure wish we could do some other things"
    )
    ( #this subshell does some other things 
        unset v #to ensure it is definitely unset
        echo "But here v is ${v-unset}: ${v:+you certainly wont see this}"
        echo "So this ${_test:-"\$_test:="} will equal NULL ${_test:="$v"}"
        ${_test:+${N:?is never substituted}}
        echo "so now we can do some other things" 
    )
    #and even though we set _test and unset v in the subshell
    echo "_test is still ${_test:-"NULL"} and ${v:+"v is still $v"}"
    # END
    CMD
v is set: and its value is not NULL
So this $_test:= will equal something you would rather do without
sh: line 7: N: so you test for it with a little nesting
But here v is unset:
So this $_test:= will equal NULL
so now we can do some other things
_test is still NULL and v is still something you would rather do without

위의 예는 POSIX 매개변수 대체의 4가지 형태와 그 다양한 형태를 모두 활용합니다.:colon null또는not null시험. 위 링크에 더 많은 정보가 있고,여기 또 간다.

내 생각엔 우리의 모습을 보여줘야 할 것 같아_test기능도 작동합니다. 그렇죠? 우리는 단지 선언empty=something함수에 대한 인수로(또는 그 이전에):

    sh <<-\CMD
    _test() { echo $( echo ${empty:?error string} ) &&\
            echo "echo still works" ; } 2<<-INIT
            ${empty?tested as a pass before function runs}
    INIT
    echo "this runs" >&2 |\
        ( empty=not_empty _test ; echo "yay! I print now!" ) ||\
            echo "suspiciously quiet"
    # END
    CMD
this runs
not_empty
echo still works
yay! I print now!

이 평가는 자체적으로 수행되므로 오류에 대한 추가 테스트가 필요하지 않습니다. 다음은 몇 가지 추가 예입니다.

    sh <<-\CMD
    empty= 
    ${empty?null, no colon, no failure}
    unset empty
    echo "${empty?this is stderr} this is not"
    # END
    CMD
sh: line 3: empty: this is stderr

    sh <<-\CMD
    _input_fn() { set -- "$@" #redundant
            echo ${*?WHERES MY DATA?}
            #echo is not necessary though
            shift #sure hope we have more than $1 parameter
            : ${*?WHERES MY DATA?} #: do nothing, gracefully
    }
    _input_fn heres some stuff
    _input_fn one #here
    # shell dies - third try doesnt run
    _input_fn you there?
    # END
    CMD
heres some stuff
one
sh: line :5 *: WHERES MY DATA?

마지막으로 원래 질문으로 돌아갑니다.$(command substitution)서브쉘에서 오류를 처리하는 방법은 무엇입니까 ?사실은 두 가지 방법이 있지만 어느 쪽도 직접적이지 않습니다. 문제의 핵심은 쉘의 평가 프로세스 - 쉘 확장(포함)입니다.$(command substitution))은 현재 셸 명령 실행보다 셸 평가 프로세스에서 더 일찍 발생합니다. 이때 오류가 포착될 수 있습니다.

작업에서 발생하는 문제는 현재 쉘이 오류로 평가될 때,$(command substitution)하위 쉘이 교체되었습니다. 오류가 없습니다.

그렇다면 두 가지 방법은 무엇입니까? 당신은$(command substitution)서브쉘 없이 동일하게 테스트하거나 그 결과를 현재 쉘 변수에 흡수하여 그 값을 테스트하십시오.

방법 1:

    echo "$(madeup && echo \: || echo '${fail:?die}')" |\
          . /dev/stdin

sh: command not found: madeup
/dev/stdin:1: fail: die

    echo $?

126

방법 2:

    var="$(madeup)" ; echo "${var:?die} still not stderr"

sh: command not found: madeup
sh: var: die

    echo $?

1

한 줄에 선언된 변수 수에 관계없이 실패합니다.

   v1="$(madeup)" v2="$(ls)" ; echo "${v1:?}" "${v2:?}"

sh: command not found: madeup
sh: v1: parameter not set

반환 값은 동일하게 유지됩니다.

    echo $?
1

이제 함정이 온다:

    trap 'printf %s\\n trap resurrects shell!' ERR
    v1="$(madeup)" v2="$(printf %s\\n shown after trap)"
    echo "${v1:?#1 - still stderr}" "${v2:?invisible}"

sh: command not found: madeup
sh: v1: #1 - still stderr
trap
resurrects
shell!
shown
after
trap

    echo $?
0

답변2

이는 bash의 버그 때문입니다. 명령 대체 단계에서

echo $( made up name )

made하위 쉘에서 실행되지만(또는 찾을 수 없음) 하위 쉘은 "최적화"되어 있으며 상위 쉘에서 특정 트랩을 사용하지 않습니다. 이건 고정됐어버전 4.4.5:

어떤 경우에는 간단한 명령이 분기를 제거하도록 최적화되어 EXIT 트랩이 실행되지 않습니다.

Bash 4.4.5 이상을 사용하면 다음 출력이 표시됩니다.

error.sh: line 13: made: command not found
err status: 127
  ! should not be reached !

트랩 처리기가 예상대로 호출되고 하위 쉘이 종료됩니다. ( set -e이렇게 하면 상위 쉘이 아닌 하위 쉘만 종료되므로 "도달하지 말았어야 했습니다"라는 메시지가 실제로 도달해야 합니다.)

이전 버전의 해결 방법은 최적화되지 않은 전체 하위 셸을 강제로 생성하는 것이었습니다.

echo $( ( made up name ) )

추가 공간은필수의산술 확장과 구별하십시오.

답변3

설정된 경우 ERR의 모든 트랩은 셸 함수, 명령 대체 및 하위 셸 환경에서 실행되는 명령에 의해 상속됩니다.

스크립트에서는 실행( echo $( made up name )) 명령입니다. Bash 명령에서 다음 중 하나로 구분합니다.;또는새로운 팀. 지휘 중

echo $( made up name )

$( made up name )주문의 일부로 간주됩니다. 이 부분이 실패하고 오류를 반환하더라도 echo이를 모르기 때문에 전체 명령이 성공하게 됩니다. 명령이 0을 반환하므로 트랩이 트리거되지 않습니다.

할당과 에코라는 두 가지 명령에 넣어야합니다.

var=$(made_up_name)
echo $var

관련 정보