조건부 오류가 발생할 때 "set -e"는 스크립트를 종료하지 않습니다.

조건부 오류가 발생할 때 "set -e"는 스크립트를 종료하지 않습니다.

다음 스크립트에는 구문 오류 또는 일부 오류가 있습니다.

#!/usr/bin/env bash
set -euo pipefail

if [ ! -f /custom.log]; then
  echo "test"
fi
abcxyz

스크립트가 출력과 함께 실패합니다.

./test.sh: line 4: [: missing `]'
./test.sh: line 7: abcxyz: command not found

이 스크립트를 수정하는 방법에는 관심이 없지만 이 오류가 발생하면 스크립트가 더 이상 계속되지 않도록 하려면 어떻게 해야 합니까? 나는 set -e이 행동이 시행되기를 기대했을 것입니다 .

답변1

set -eif// 구성의 조건부 부분이나 a의 왼쪽 측면, 또는 함수 내, 서브셸, 소스 파일, 이러한 조건에서 호출되는 ed 코드 와 같이 조건으로 사용된 명령이 실패하면 실행되지 않습니다 .whileuntil||&&eval

이것이 실제로 그렇다면:

if [ ! -f /custom.log ]; then

/custom.log일반 파일인 경우 스크립트가 종료되고 [0이 아닌 종료 상태로 종료됩니다.

테스트 조건이 충족되지 않고 구문 오류가 있는 경우(그러나 모든 구문 오류는 아님, 예를 들어 in이 아님), 쉘 [의 내장 명령( bash및 대부분의 다른 구현)은 상태로 종료됩니다.12[ -v 'a[+]' ]POSIX에서는 오류가 발생할 경우를 대비해 종료 상태가 1보다 커야 합니다..

따라서 명령이 1보다 큰 코드로 종료되면 조건부 사용 여부에 관계없이 스크립트를 종료하도록 선택할 수 있습니다. 예를 들면 다음과 같습니다.

shopt -s extdebug # make sure the DEBUG trap propagates to subshells
trap '(($?>1 && (ret=$?))) && exit "$ret"' DEBUG
[ -f / ] || echo / not a regular file # OK
[ -f /] || echo was a syntax error # causes an exit, not output
echo not reached

트랩은 종료를 트리거하는 동일한 조건에서만 실행되므로 ERR이를 위해 트랩을 사용할 수 없습니다 .ERRset -e

이제 그 의미를 알아두십시오. 예를 들어, 결과는 다음과 같습니다.

if grep -qs pattern /file; then
  echo pattern was found in /file
fi

/file존재하지 않거나 읽을 수 없으면 종료합니다. grep이 경우 반환 상태는 2입니다. 비록 사용되더라도 -s분명히 이러한 경우를 무시하려는 의도입니다.

따라서 조건에 사용하는 명령이 1보다 큰 상태로 종료될 수 있는 조건을 알고 있어야 합니다. 이러한 문제를 해결하려면 다음과 같은 것이 필요합니다.

if sh -c 'grep -sq pattern / file || exit 1'; then...

제한할 수 있습니다종료 상태가 1보다 크면 종료됩니다.[or 명령 에 연결합니다 test. 예를 들면 다음과 같습니다.

unset -v previous_BASH_COMMAND
trap '
  case $previous_BASH_COMMAND in
    ("[ "* | "test "*) (($?>1 && (ret=$?))) && exit "$ret"
  esac
  previous_BASH_COMMAND=$BASH_COMMAND' DEBUG

여기에는 몇 가지 제한 사항이 있습니다. 존재하다

echo x
([ -f/]; echo y)

이렇게 하면 하위 쉘이 종료되지만 상위 쉘은 $previous_BASH_COMMAND거기에 설정되지 않았기 때문에 종료되지 않습니다. 그리고:

[ -f / ] && echo a regular file
(grep -qs foo /file && echo foo in /file)
echo here

will 은 2이고 was echo here이므로 실행 중에 쉘이 종료됩니다 .$?$previous_BASH_COMMAND[ -f / ]

아무튼 이런거

[ -f /] | cat
export var="$([ -f /])"

종료 상태가 상위 쉘 프로세스에 전파되지 않기 때문에 종료 상태를 감지할 수 없습니다( pipefail첫 번째 경우의 옵션 제외).

이제 버그는 개발 시(스크립트 작성 및 테스트 시) 쉽게 감지되므로 런타임에 이 (취약한) 감지를 추가할 가치가 있는지 잘 모르겠습니다.

관련 정보