..png)
이에 따르면참고 도서:
-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