"-o errtrace"를 사용하여 명령 대체에서 트래핑 오류 (예 : set -E)


14

참조 설명서 에 따르면 :

-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 ! "

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

되어 -E/-o errtrace위의 코드와 같이 작업하기로? 아니면 가장 잘 읽었습니까?


2
좋은 질문입니다. 로 교체 하면 원하는 동작 echo $( made up name )$( made up name )생성됩니다. 그래도 설명이 없습니다.
iruvar

bash의 -E는 모르지만 파이프 라인의 마지막 명령에서 오류가 발생하면 -e가 쉘 종료에만 영향을 미친다는 것을 알고 있습니다. 따라서 귀하 var=$( pipe )$( pipe )예제는 모두 파이프 끝점을 나타내지 만 pipe > echo그렇지 않습니다. "1. 다중 명령 파이프 라인에서 개별 명령이 실패해도 쉘이 종료되지 않습니다. 파이프 라인 자체의 실패 만 고려됩니다."
mikeserv

echo $ ($ {madeupname?}) : 실패 할 수 있습니다. 그러나 그것은 설정되었습니다. 다시, -E는 내 경험 밖입니다.
mikeserv

@mikeserv @ 1_CR bash 매뉴얼 @ echoecho항상 0을 반환 한다는 것을 나타냅니다. 이것은 분석에 반영되어야합니다.

답변:


5

참고 : zsh여기에서 대부분의 예제에 대해 "인라인 주석"을 허용하도록 구성하지 않으면 "불량 패턴"에 대해 불평하고 프록시 쉘을 통해 실행하지 않습니다 sh <<-\CMD.

자, 위의 의견에서 언급했듯이 bash에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()shecho "this doesnt even print"

아마 그것은 당신이 원하는 것이 아닙니다 .

때문입니다 ${var?}스타일의 파라미터 확장이되어 을 종료하도록 설계shell 누락 된 매개 변수의 경우에는, 다음과 같이 작동합니다 :

${parameter:?[word]}

만약 에러 나타내 NullUnset.파라미터가 설정 또는 널이면 expansion of word한다 (또는이를 나타내는 메시지는 단어가 생략 된 경우를 unset)을 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)은 현재 쉘 명령 실행보다 쉘 평가 프로세스의 초기에 발생하는 쉘의 평가 프로세스입니다 (오류 포함 및 오류 포함).

op가 경험하는 문제는 현재 쉘이 오류를 평가할 때까지 $(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

예를 들어, 그의 정확한 사양은 다음과 같습니다. echo $ {v = $ (madeupname)}; echo "$ {v :? trap1st}!에 도달하지 않았습니다!"
mikeserv

1
요약 :이 매개 변수 확장은 변수 설정 등을 제어하는 ​​좋은 방법입니다. echo의 종료 상태와 관련하여, 나는 echo "abc" "${v1:?}"실행되지 않는 것 같습니다 (abc는 인쇄되지 않음). 그리고 쉘은 1을 반환합니다. 이것은 명령 짝수와 함께 또는 "${v1:?}"CLI 없이 직접 적용됩니다. 그러나 OP의 스크립트의 경우 트랩을 트리거하는 데 필요한 모든 것은 존재하지 않는 명령의 대체를 포함하는 변수 할당을 한 줄에 배치하는 것입니다. 그렇지 않으면 echo의 동작은 설명 한 테스트와 같이 중단되지 않는 한 항상 0을 반환하는 것입니다.

그냥 v=$( madeup ). 나는 그것이 안전하지 않은 것을 보지 못합니다. 그것은 단지 과제 일뿐 v="$(lss)"입니다. 예를 들어 사람이 명령의 철자를 잘못 입력했습니다 . 오류입니다. 예, 마지막 명령 $의 오류 상태로 확인할 수 있습니까? -그것은 줄의 명령 (명령 이름이없는 할당)이기 때문에 반향의 인수가 아닙니다. 또한 여기에! = 0과 같은 함수가 포함되어 있으므로 피드백을 두 번 얻을 수 있습니다. 그렇지 않으면 분명히 프레임 워크 내 에서이 순서대로 수행하는 더 좋은 방법이 있지만 OP에는 하나의 줄이 있습니다 : 에코와 대체 실패. 그는 반향이 궁금합니다.

예, 질문에 간단한 과제가 주어지고 당연한 것으로 언급되었으며 -E를 사용하는 방법에 대한 구체적인 조언을 제공 할 수는 없었지만 입증 된 문제와 비슷한 결과를 보여 주려고 노력했지만 bashisms. 어쨌든, 특히 라인 당 단일 할당과 같이 언급 한 문제는 이러한 솔루션을 파이프 라인에서 처리하기 어렵게 만들고 처리 방법을 보여주었습니다. 당신이 말하는 것은 사실이지만 단순히 그것을 할당하는 것에 대해 안전하지 않은 것은 없습니다.
mikeserv

1
좋은 지적-아마도 더 많은 노력이 필요합니다. 내가 생각 할게
mikeserv

1

설정된 경우, ERR의 트랩은 쉘 기능, 명령 대체 및 서브 쉘 환경에서 실행되는 명령에 의해 상속됩니다.

스크립트에서 명령 실행 ( echo $( made up name )). bash에서 명령은 ; 로 구분됩니다 . 또는 새로운 줄로 . 명령에서

echo $( made up name )

$( made up name )명령의 일부로 간주됩니다. 이 부분이 실패하고 오류와 함께 반환 되더라도 전체 명령은 echo알 수없는 대로 성공적으로 실행 됩니다. 트랩이 트리거되지 않은 상태에서 명령이 0으로 리턴 될 때

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

var=$(made_up_name)
echo $var

echo $var실패하지 않습니다- 실제로 살펴보기 $var전에 빈으로 확장됩니다 echo.
mikeserv

0

이것은 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 ) )

산술 확장과 구별하기 위해 추가 공간이 필요 합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.