%EB%A5%BC%20%EC%88%98%EC%A0%95%ED%95%98%EB%8A%94%20%EC%89%98%EC%9D%84%20%EC%9E%91%EC%84%B1%ED%95%98%EB%A0%A4%EA%B3%A0%20%EC%8B%9C%EB%8F%84%ED%95%9C%20%EC%82%AC%EB%9E%8C%EC%9D%B4%20%EC%9E%88%EC%8A%B5%EB%8B%88%EA%B9%8C%3F.png)
errexit( set -e
)은 간단한 스크립트를 더욱 강력하게 만드는 방법으로 종종 제안됩니다. 그러나 이는 일반적으로 더 복잡한 스크립트, 특히 함수에서 어떻게 작동하는지 본 사람에게는 끔찍한 함정으로 간주됩니다. 그리고 서브쉘에서도 매우 유사한 문제가 나타날 수 있습니다. 다음 예를 들어보세요.https://stackoverflow.com/questions/29926013/exit-subshell-on-error
(
set -o errexit
false
true
) && echo "OK" || echo "FAILED";
여기서 함정은 쉘이 "FAILED"가 아닌 "OK"를 표시한다는 것입니다.[*]
특정 사례 의 동작은 set -e
역사적으로 불행한 방식으로 다양했지만 현대 배포판에서 사용 가능한 쉘은 POSIX 쉘 표준을 따른다고 주장하며 테스트에서는 다음 모두에 대해 동일한 동작("OK")을 보여주었습니다.
bash-4.4.19-2.fc28.x86_64
(페도라)busybox-1.26.2-3.fc27.x86_64
(페도라)dash 0.5.8-2.4
(데비안 9)zsh 5.3.1-4+b2
(데비안 9)posh 0.12.6+b1
(데비안 9)ksh 93u+20120801-3.1
(데비안 9).
질문
위 코드에 대해
sh
인쇄한다는 점을 제외하고 POSIX와 유사한 기능을 가진 셸을 작성할 수 없는 기술적 이유가 있습니까 ?FAILED
false를 반환하는
set -e
최상위 표현식이 치명적인 것으로 간주 되도록 변경되기를 바랍니다 .&&
https://serverfault.com/a/847016/133475 그리고 grep과 함께 사용할 수 있는 더 좋은 관용구가 있기를 바랍니다.set -e # print matching lines, but it's not an error if there are no matches in the file ( set +e grep pattern file.txt RET=$? [[ $RET == 0 || $RET == 1 ]] || exit $RET )
let
나는 또한 숫자 값을 암시적으로 true/false 종료 상태로 강제하는 것을 방지하는 방식으로 산술 명령문(내장)이 재정의될 것이라고 가정합니다 .이 작업을 수행하는 쉘의 예가 있습니까?
Bourne과 다른 구문을 사용하는 것을 보는 것은 괜찮습니다. 나는 짧은 "접착제" 스크립트를 작성할 때 간결하면서도 오류 감지를 위한 간결한 전략을 갖고 있는 것에 관심이 있습니다. 나는 문 구분 기호로 사용하는 것을 좋아하지 않습니다
&&
. 실수로 문 구분 기호를 생략하면 오류가 자동으로 무시됩니다.
[*] 편집하다. 이것은 아마도 사용하기에 완벽한 예가 아닐 수도 있습니다. 토론에 따르면 더 나은 예는 set -o errexit
하위 쉘 위로 이동하는 것입니다.
(false; echo foo) || echo bar; echo \ $?
AFAICT 이것은 표의 테스트 사례와 매우 유사합니다.여기, 이는 원래 Bourne 셸과 그 하위 항목 대부분에 "foo"(그리고 "0")가 표시된다는 의미입니다. "hist.ash"와 bash 1.14.7은 예외입니다.
set -e
서브셸 내부에 추가할 때 (set -e; false; echo foo) || echo bar; echo \ $?
"SVR4 sh sun5.10"과 dash-0.3.4라는 두 가지 추가 예외가 있습니다.
내 질문의 정신에 따르면, set -e
서브쉘 내부에 있는 것이 방해가 되었다고 생각합니다. 나의 주요 관심은 set -e
스크립트 상단에서 사용하는 관용구에 있으며 이는 하위 쉘에도 적용됩니다(이미 POSIX 동작임).
Jörg Schilling은 원래 Unix의 Bourne 쉘이 내가 이 질문에서 사용한 예에 대해 "FAILED"를 인쇄할 것이라고 제안했으며, 그는 이 쉘을 다음과 같이 POSIX로 포팅했습니다.schilytools의 "osh"이며, 이 결과는 2018-06-11 릴리스 기준으로 검증되었습니다. 아마도 이는 1) set -e
서브쉘 내부에 있는 것과 2) "SVR4 sh sun5.10"을 기반으로 한 것 같습니다 . "osh"는 "OpenSolaris 소스를 기반으로 하므로 SVR4 및 SVID3을 기반으로 합니다". 또는 && echo "OK"
서브쉘과 || echo "FAILED"
.
나는 schily 껍질이 내 질문에 대답하지 않는다고 생각합니다. 함수에 서브셸을 사용할 수 있고( 서브셸에서 함수를 실행하기 위해 (
대신 사용) 모든 서브셸을 . 그러나 서브셸을 사용하면 전역 변수를 수정할 수 없다는 제한이 발생합니다. 적어도 나는 이것을 범용 코딩 스타일로 옹호하는 사람을 아직 본 적이 없습니다.{
set -e
답변1
그만큼POSIX 표준(참조-이자형) 쉘 옵션에 대해서는 Bash 매뉴얼보다 더 명확합니다 errexit
.
이 옵션이 켜져 있으면 명령이 실패할 때(셸 오류의 결과에 나열된 이유 또는 0보다 큰 종료 상태를 반환하여) 마치 종료 특수 내장 유틸리티를 실행하는 것처럼 셸이 즉시 종료됩니다. 인수가 없습니다. 단, 다음은 예외입니다.
다중 명령 파이프라인에서 개별 명령이 실패하더라도 셸이 종료되지는 않습니다. 파이프라인 자체의 고장만 고려되어야 합니다.
-e 설정은 !로 시작하는 파이프라인인 while, Until, if 또는 elif 예약어 뒤에 오는 복합 목록을 실행할 때 무시됩니다. 예약어 또는 마지막 이외의 AND-OR 목록 명령.
하위 쉘 명령이 아닌 복합 명령의 종료 상태가 -e가 무시되는 동안 실패의 결과인 경우 -e는 이 명령에 적용되지 않습니다.
이 요구 사항은 셸 환경과 각 하위 셸 환경에 별도로 적용됩니다. 예를 들어:
set -e; (false; echo one) | cat; echo two
false 명령을 사용하면 echo one을 실행하지 않고 서브쉘이 종료됩니다. 그러나 파이프라인의 종료 상태(false; echo one) | 고양이는 제로입니다.
분명히 샘플 코드는 다음 의사 코드와 동일합니다.
( LIST; ) && COMMAND || COMMAND
종료 상태목록 다음과 같은 이유로 0입니다.
쉘
errexit
옵션은 무시됩니다.반환 상태는 다음에 지정된 마지막 명령의 종료 상태입니다.목록, 여기
true
.
따라서 AND 목록의 두 번째 부분인 가 실행됩니다 echo "OK"
.
답변2
'... 글을 쓰려고 한 사람이 있나요?'라는 질문에 대한 대답이 아니라, 그것이 어떻게 이루어질 수 있는지에 대한 몇 가지 생각입니다. 누구든지 피드백을 제공할 수 있는지 궁금합니다.
현재 스크립팅 솔루션(Python, JavaScript, 이전 Perl 등)의 일반적인 접근 방식은 try ... catch 블록을 사용하는 것입니다. 예상되는 동작은 모든 오류가 예외를 트리거하는 것입니다. 이 동작은 오류가 $?에 캡처되는 *sh 동작과 충돌합니다. 분명히 '-e'는 대부분의 개발자가 기대하는 대로 작동하는 방식으로 올바르게 작동하지 못했습니다. 이미 광범위하게 문서화되어 있으므로 여기서 자세히 설명할 필요가 없습니다.
두 가지 가능한 접근 방식:
- 더 나은 것을 만드십시오
set -e
: 예를 들어set -ocatcherr
더 나은 오류 처리를 사용합니다. try { list ; }
최신 스크립트 솔루션에 따라 새 명령을 추가합니다 .
'-ofailerr'은 다음과 같이 정의할 수 있습니다.
- 명령이 제어 흐름 외부(if/while/until)에서 실패하면 현재 실행 컨텍스트는 1을 반환해야 합니다(또는 최상위 블록인 경우 종료해야 합니다). 이 변경으로 인해 개발자는 실패한 명령에 대한 오류 처리를 제공하거나 무시할 수 있는 오류가 있는 명령을 명시적으로 표시하게 됩니다.
- 간단한 스크립트에 대해 효과적으로 작동하며 처리되지 않은 오류가 발생하면 중단됩니다.
접근 try { list ; }
방식은 비슷하지만 구문만 다릅니다.
예:
function foo {
cat $* > a.txt
wc -l a.txt
}
set -oerrfail
foo /non/existing/file # Will fail, without executing the "wc -l"
if foo /non/existing/file ; then
...
else
# This is the "catch" block
fi
try 블록을 사용하면 다음과 같습니다.
try {
foo /non/existing/file
...
} || { catch-block }
실제로 try는 'if !' 아래에서 명령 목록을 실행하는 "set -oerofail"과 동일합니다. { ... } ; 그런 다음 { 캐치 블록 }