development

Bash 스크립트에서 오류 발생

big-blog 2020. 10. 12. 07:51
반응형

Bash 스크립트에서 오류 발생


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

예를 들면 :

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

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

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

echo "Error!" > logfile.log
exit 125

또는 다음 :

echo "Error!" 1>&2
exit 64

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

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

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


기본 오류 처리

테스트 케이스 실행기가 실패한 테스트에 대해 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


관련 게시물


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

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

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

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

수행하려는 작업에 따라 사용할 수있는 셸 옵션이 있습니다. 첫 번째 경우 쉘은 옵션을 제공 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)
    

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

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

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

For elaborated functionality, have the trap call a processing function. You can always use a case statement on your arg ($_) if you need to do more cleanup, etc. Assign to a var for a little syntactic sugar -

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

Sample output:

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

Obviously, you could

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

Lots of room for design improvement.

The draw backs include the fact that false isn't pretty (thus the sugar), and other things tripping the trap might look a little stupid. Still, I like this method.


You have 2 options: Redirect the output of the script to a file, Introduce a log file in the script and

  1. Redirecting output to a file:

Here you assume that the script outputs all necessary info, including warning and error messages. You can then redirect the output to a file of your choice.

./runTests &> output.log

The above command redirects both the standard output and the error output to your log file.

Using this approach you don't have to introduce a log file in the script, and so the logic is a tiny bit easier.

  1. Introduce a log file to the script:

In your script add a log file either by hard coding it:

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

or passing it by a parameter:

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

It's a good idea to add the timestamp at the time of execution to the log file at the top of the script:

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

You can then redirect your error messages to the log file

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

This will append the error to the log file and continue execution. If you want to stop execution when critical errors occur, you can exit the script:

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

Note that exit 1 indicates that the program stop execution due to an unspecified error. You can customize this if you like.

Using this approach you can customize your logs and have a different log file for each component of your script.


If you have a relatively small script or want to execute somebody else's script without modifying it the the first approach is more suitable.

If you always want the log file to be at the same location, this is the better option of the 2. Also if you have created a big script with multiple components then you may want to log each part differently and the second approach is your only option.


I often find it useful to write a function to handle error messages so the code is cleaner overall.

# 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
}

This takes the error code from the previous command and uses it as the default error code when exiting the whole script. It also notes the time, with microseconds where supported (GNU date's %N is nanoseconds, which we truncate to microseconds later).

If the first option is zero or a positive integer, it becomes the exit code and we remove it from the list of options. We then report the message to standard error, with the name of the script, the word "ERROR", and the time (we use parameter expansion to truncate nanoseconds to microseconds, or for non-GNU times, to truncate e.g. 12:34:56.%N to 12:34:56). A colon and space are added after the word ERROR, but only when there is a provided error message. Finally, we exit the script using the previously determined exit code, triggering any traps as normal.

Some examples (assume the code lives in 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"

참고URL : https://stackoverflow.com/questions/30078281/raise-error-in-a-bash-script

반응형