Bash 스크립트에서 오류 발생


103

Bash 스크립트에서 "Test cases Failed !!!"메시지와 함께 오류를 발생시키고 싶습니다. Bash에서 어떻게할까요?

예를 들면 :

if [ condition ]; then
    raise error "Test cases failed !!!"
fi

1
이 오류에 대해 어떻게 하시겠습니까? 스크립트는 어떻게 호출됩니까? 하나의 스크립트입니까 아니면 여러 스크립트입니까? 스크립트의 용도는 어떻게됩니까?
Etan Reisner 2015 년

하나의 스크립트입니다. 나는 ./script/test.sh처럼 우분투 터미널을 사용하여 전화
나빈 쿠마르에게



에 대한 사랑이 echo you screwed up at ... | mail -s BUG $bugtrackeremailaddress없습니까?

답변:


121

이는 오류 메시지를 저장할 위치에 따라 다릅니다.

다음을 수행 할 수 있습니다.

echo "Error!" > logfile.log
exit 125

또는 다음 :

echo "Error!" 1>&2
exit 64

예외가 발생하면 프로그램 실행이 중지됩니다.

당신은 또한 같은 수 있습니다 exit xxx어디에 xxx당신이 (0 ~ 255) 운영 체제로 복귀 할 수있는 오류 코드입니다. 이곳까지 12564당신이 종료 할 수 있습니다 무작위 코드입니다. 프로그램이 비정상적으로 중지되었음을 OS에 표시해야하는 경우 (예 : 오류 발생) 0이 아닌 종료 코드 를 에 전달해야 합니다 exit.

@chepner가 지적했듯이 할 수 있습니다 . 이는 지정되지 않은 오류를exit 1 의미 합니다 .


12
또는 오류가 발생하는 stderr로 보낼 수 있습니다.

stderr로 보내는 방법?
Naveen Kumar

2
@ user3078630, 방금 내 대답을 편집했습니다. 1>&2트릭을 할 것입니다
ForceBru

오류 인 경우 0이 아닌 종료 상태로 종료 해야 합니다. exit자체적으로 가장 최근에 완료된 명령의 종료 상태 (0
chepner

3
특정 의미를 염두에 두지 않는 한을 사용해야합니다 exit 1. 이는 관례 상 지정되지 않은 오류를 의미합니다.
chepner

36

기본 오류 처리

테스트 케이스 실행기가 실패한 테스트에 대해 0이 아닌 코드 를 반환하는 경우 다음 과 같이 간단히 작성할 수 있습니다.

test_handler test_case_x; test_result=$?
if ((test_result != 0)); then
  printf '%s\n' "Test case x failed" >&2  # write error message to stderr
  exit 1                                  # or exit $test_result
fi

또는 더 짧게 :

if ! test_handler test_case_x; then
  printf '%s\n' "Test case x failed" >&2
  exit 1
fi

또는 가장 짧은 :

test_handler test_case_x || { printf '%s\n' "Test case x failed" >&2; exit 1; }

test_handler의 종료 코드로 종료하려면 :

test_handler test_case_x || { ec=$?; printf '%s\n' "Test case x failed" >&2; exit $ec; }

고급 오류 처리

보다 포괄적 인 접근 방식을 원할 경우 오류 처리기를 사용할 수 있습니다.

exit_if_error() {
  local exit_code=$1
  shift
  [[ $exit_code ]] &&               # do nothing if no error code passed
    ((exit_code != 0)) && {         # do nothing if error code is 0
      printf 'ERROR: %s\n' "$@" >&2 # we can use better logging here
      exit "$exit_code"             # we could also check to make sure
                                    # error code is numeric when passed
    }
}

그런 다음 테스트 케이스를 실행 한 후 호출하십시오.

run_test_case test_case_x
exit_if_error $? "Test case x failed"

또는

run_test_case test_case_x || exit_if_error $? "Test case x failed"

다음과 같은 오류 처리기의 장점 exit_if_error은 다음 과 같습니다.

  • 로깅 , 스택 추적 인쇄 와 같은 모든 오류 처리 로직을 표준화 할 수 있습니다. , 알림, 정리 수행 를 한 곳에서 .
  • 에러 핸들러가 에러 코드를 인자로 받도록함으로써, 우리는 if에러에 대한 종료 코드를 테스트 하는 복잡한 블록 으로부터 호출자를 보호 할 수 있습니다.
  • 시그널 핸들러 ( trap 사용 )가 있으면 거기에서 오류 핸들러를 호출 할 수 있습니다.

오류 처리 및 로깅 라이브러리

다음은 오류 처리 및 로깅의 완전한 구현입니다.

https://github.com/codeforester/base/blob/master/lib/stdlib.sh


관련 게시물


9

이 문제에 접근 할 수있는 몇 가지 방법이 더 있습니다. 요구 사항 중 하나가 몇 가지 셸 명령이 포함 된 셸 스크립트 / 함수를 실행하고 스크립트가 성공적으로 실행되었는지 확인하고 실패시 오류를 발생시키는 것이라고 가정합니다.

의 셸 명령은 일반적으로 반환 된 종료 코드에 의존하여 예상치 못한 이벤트로 인해 성공했는지 실패했는지 셸에 알립니다.

그래서 당신이 원하는 것은이 두 가지 범주에 속합니다.

  • 오류시 종료
  • 오류시 종료 및 정리

수행하려는 작업에 따라 사용할 수있는 셸 옵션이 있습니다. 첫 번째 경우 쉘은 옵션을 제공 set -e하고 두 번째 경우에는 다음을 수행 할 수 있습니다 trap.EXIT

exit내 스크립트 / 함수에서 사용해야합니까 ?

사용 exit일반적으로 하면 가독성이 향상됩니다. 특정 루틴에서 답을 알고 나면 즉시 호출 루틴을 종료하려고합니다. 루틴이 오류를 감지 한 후 더 이상 정리할 필요가없는 방식으로 정의 된 경우 즉시 종료하지 않으면 더 많은 코드를 작성해야합니다.

따라서 스크립트를 정리하기 위해 스크립트에서 정리 작업을 수행해야하는 경우에는를 사용 하지 않는 것이 좋습니다 exit.

종료시 set -e오류에 사용해야 합니까?

아니!

set -e쉘에 "자동 오류 감지"를 추가하려는 시도였습니다. 그 목표는 오류가 발생할 때마다 쉘이 중단되도록하는 것이었지만 예를 들어 많은 잠재적 인 함정이 있습니다.

  • if 테스트의 일부인 명령은 면역입니다. 이 예에서 test존재하지 않는 디렉토리 에 대한 검사에서 중단 될 것으로 예상하면 그렇지 않으며 else 조건으로 이동합니다.

    set -e
    f() { test -d nosuchdir && echo no dir; }
    f
    echo survived
  • 마지막 명령이 아닌 파이프 라인의 명령은 면역입니다. 아래 예에서는 가장 최근에 실행 된 (가장 오른쪽) 명령의 종료 코드가 ( cat) 로 간주 되고 성공했기 때문입니다. 이것은 set -o pipefail옵션 으로 설정함으로써 피할 수 있지만 여전히 경고입니다.

    set -e
    somecommand that fails | cat -
    echo survived 

사용 권장- trap종료시

평결은을 사용하는 대신 맹목적으로 종료하는 대신 오류를 처리 할 수 ​​있도록 의사 신호 에 set -ea trap를 사용하는 것 입니다 ERR.

ERR트랩은 쉘 자체가 아닌 에러 코드와 함께 종료 할 때 코드를 실행하지만,하지 때 조건의 일부 (경우처럼 아니라고 쉘에 의한 명령의 실행 cmd, 또는 cmd ||0이 아닌 종료 상태) 종료 .

일반적인 관행은 어떤 라인과 종료 원인에 대한 추가 디버그 정보를 제공하기 위해 트랩 핸들러를 정의하는 것입니다. ERR신호 를 유발 한 마지막 명령의 종료 코드는 이 시점에서 계속 사용할 수 있습니다.

cleanup() {
    exitcode=$?
    printf 'error condition hit\n' 1>&2
    printf 'exit code returned: %s\n' "$exitcode"
    printf 'the command executing at the time of the error was: %s\n' "$BASH_COMMAND"
    printf 'command present on line: %d' "${BASH_LINENO[0]}"
    # Some more clean up code can be added here before exiting
    exit $exitcode
}

실패한 스크립트 위에 다음과 같이이 핸들러를 사용합니다.

trap cleanup ERR

이것을 false15 행에 포함 된 간단한 스크립트에 합치면 다음과 같은 정보를 얻을 수 있습니다.

error condition hit
exit code returned: 1
the command executing at the time of the error was: false
command present on line: 15

trap또한 오류에 관계없이 신호에서 쉘 완료 (예 : 쉘 스크립트 종료)시 정리를 실행하는 옵션을 제공합니다 EXIT. 동시에 여러 신호를 트랩 할 수도 있습니다. 트랩에 지원되는 신호 목록은 trap.1p-Linux 매뉴얼 페이지 에서 찾을 수 있습니다 .

주의해야 할 또 다른 사항은 하위 셸을 다루는 경우 제공된 메서드가 작동하지 않는다는 점을 이해하는 것입니다.이 경우 자체 오류 처리를 추가해야 할 수도 있습니다.

  • 하위 셸에서 set -e작동하지 않습니다. 는 false서브 쉘로 제한되고 부모 쉘에 전달되지 없구요. 여기에서 오류 처리를 수행하려면 자신의 논리를 추가하여(false) || false

    set -e
    (false)
    echo survived
  • 또한 마찬가지 trap입니다. 아래 논리는 위에서 언급 한 이유로 작동하지 않습니다.

    trap 'echo error' ERR
    (false)

5

다음은 STDERR에 실패한 모든 항목의 마지막 인수를 인쇄하고 실패한 행을보고하며 종료 코드로 행 번호를 사용하여 스크립트를 종료하는 간단한 트랩입니다. 이것들이 항상 훌륭한 아이디어는 아니지만 이것은 당신이 만들 수있는 몇 가지 창의적인 응용 프로그램을 보여줍니다.

trap 'echo >&2 "$_ at $LINENO"; exit $LINENO;' ERR

나는 그것을 테스트하기 위해 루프가있는 스크립트에 넣었습니다. 난 그냥 임의의 숫자에 대한 히트를 확인합니다. 실제 테스트를 사용할 수 있습니다. 보석금이 필요한 경우 던지고 싶은 메시지와 함께 false (트랩을 트리거 함)를 호출합니다.

정교한 기능을 위해 트랩이 처리 함수를 호출하도록하십시오. 더 많은 정리 작업이 필요한 경우 항상 arg ($ _)에 case 문을 사용할 수 있습니다. 약간의 구문 설탕을 위해 var에 할당합니다.

trap 'echo >&2 "$_ at $LINENO"; exit $LINENO;' ERR
throw=false
raise=false

while :
do x=$(( $RANDOM % 10 ))
   case "$x" in
   0) $throw "DIVISION BY ZERO" ;;
   3) $raise "MAGIC NUMBER"     ;;
   *) echo got $x               ;;
   esac
done

샘플 출력 :

# bash tst
got 2
got 8
DIVISION BY ZERO at 6
# echo $?
6

분명히, 당신은 할 수 있습니다

runTest1 "Test1 fails" # message not used if it succeeds

디자인 개선의 여지가 많습니다.

단점은 false예쁘지 않다는 사실 (따라서 설탕)과 함정을 넘어 뜨리는 다른 것들이 약간 어리석게 보일 수 있다는 사실을 포함합니다 . 그래도이 방법이 마음에 듭니다.


4

두 가지 옵션이 있습니다. 스크립트 출력을 파일로 리디렉션하고, 스크립트에 로그 파일을 추가하고,

  1. 출력을 파일로 리디렉션 :

여기서는 스크립트가 경고 및 오류 메시지를 포함하여 필요한 모든 정보를 출력한다고 가정합니다. 그런 다음 출력을 원하는 파일로 리디렉션 할 수 있습니다.

./runTests &> output.log

위의 명령은 표준 출력과 오류 출력을 모두 로그 파일로 리디렉션합니다.

이 접근 방식을 사용하면 스크립트에 로그 파일을 도입 할 필요가 없으므로 논리가 조금 더 쉽습니다.

  1. 스크립트에 로그 파일을 소개합니다 .

스크립트에서 하드 코딩하여 로그 파일을 추가합니다.

logFile='./path/to/log/file.log'

또는 매개 변수로 전달 :

logFile="${1}"  # This assumes the first parameter to the script is the log file

스크립트 상단에있는 로그 파일에 실행 시점의 타임 스탬프를 추가하는 것이 좋습니다.

date '+%Y%-m%d-%H%M%S' >> "${logFile}"

그런 다음 오류 메시지를 로그 파일로 리디렉션 할 수 있습니다.

if [ condition ]; then
    echo "Test cases failed!!" >> "${logFile}"; 
fi

그러면 로그 파일에 오류가 추가되고 실행이 계속됩니다. 심각한 오류가 발생할 때 실행을 중지하려면 exit다음 스크립트 를 사용할 수 있습니다 .

if [ condition ]; then
    echo "Test cases failed!!" >> "${logFile}"; 
    # Clean up if needed
    exit 1;
fi

참고 exit 1 인해 알 수없는 오류 해당 프로그램 중지 실행을 나타냅니다. 원하는 경우이를 사용자 정의 할 수 있습니다.

이 접근 방식을 사용하면 로그를 사용자 정의하고 스크립트의 각 구성 요소에 대해 다른 로그 파일을 가질 수 있습니다.


비교적 작은 스크립트가 있거나 다른 사람의 스크립트를 수정하지 않고 실행하려는 경우 첫 번째 방법이 더 적합합니다.

로그 파일이 항상 같은 위치에 있도록하려면 2보다 나은 옵션입니다. 또한 여러 구성 요소가 포함 된 큰 스크립트를 만든 경우 각 부분을 다르게 기록 할 수 있으며 두 번째 방법은 유일한 방법입니다. 선택권.


3

오류 메시지를 처리하는 함수를 작성하면 코드가 전반적으로 더 깔끔해집니다.

# Usage: die [exit_code] [error message]
die() {
  local code=$? now=$(date +%T.%N)
  if [ "$1" -ge 0 ] 2>/dev/null; then  # assume $1 is an error code if numeric
    code="$1"
    shift
  fi
  echo "$0: ERROR at ${now%???}${1:+: $*}" >&2
  exit $code
}

이것은 이전 명령의 오류 코드를 가져 와서 전체 스크립트를 종료 할 때 기본 오류 코드로 사용합니다. 또한 지원되는 경우 마이크로 초로 시간을 기록합니다 (GNU 날짜 %N는 나노초이며 나중에 마이크로 초로 자릅니다).

첫 번째 옵션이 0이거나 양의 정수이면 종료 코드가되고 옵션 목록에서 제거됩니다. 우리는 다음 스크립트의 이름으로, 표준 오류 메시지를보고, 단어 "ERROR", 시간 (우리는 마이크로에 잘라 내기 나노초에 매개 변수 확장을 사용하거나 비 GNU 시간에 대한, 잘라 내기 예에 12:34:56.%N12:34:56). ERROR라는 단어 뒤에 콜론과 공백이 추가되지만 제공된 오류 메시지가있는 경우에만 추가됩니다. 마지막으로 이전에 결정된 종료 코드를 사용하여 스크립트를 종료하고 모든 트랩을 정상적으로 트리거합니다.

몇 가지 예 (코드가에 있다고 가정 script.sh) :

if [ condition ]; then die 123 "condition not met"; fi
# exit code 123, message "script.sh: ERROR at 14:58:01.234564: condition not met"

$command |grep -q condition || die 1 "'$command' lacked 'condition'"
# exit code 1, "script.sh: ERROR at 14:58:55.825626: 'foo' lacked 'condition'"

$command || die
# exit code comes from command's, message "script.sh: ERROR at 14:59:15.575089"
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.