programing

Bash에서의 에러 처리

golfzon 2023. 4. 9. 22:37
반응형

Bash에서의 에러 처리

Bash의 오류를 처리하기 위해 가장 좋아하는 방법은 무엇입니까?제가 웹에서 발견한 오류 처리의 가장 좋은 예는 http://www.linuxcommand.org에 있는 William Shotts 주니어가 쓴 것입니다.

Bash에서의 에러 처리에는, 다음의 함수를 사용하는 것을 추천합니다.

#!/bin/bash

# A slicker error handling routine

# I put a variable in my scripts named PROGNAME which
# holds the name of the program being run.  You can get this
# value from the first item on the command line ($0).

# Reference: This was copied from <http://www.linuxcommand.org/wss0150.php>

PROGNAME=$(basename $0)

function error_exit
{

#   ----------------------------------------------------------------
#   Function for exit due to fatal program error
#       Accepts 1 argument:
#           string containing descriptive error message
#   ---------------------------------------------------------------- 

    echo "${PROGNAME}: ${1:-"Unknown Error"}" 1>&2
    exit 1
}

# Example call of the error_exit function.  Note the inclusion
# of the LINENO environment variable.  It contains the current
# line number.

echo "Example of error with line number and message"
error_exit "$LINENO: An error has occurred."

Bash 스크립트에서 사용하는 더 나은 오류 처리 루틴이 있습니까?

덫을 써!

tempfiles=( )
cleanup() {
  rm -f "${tempfiles[@]}"
}
trap cleanup 0

error() {
  local parent_lineno="$1"
  local message="$2"
  local code="${3:-1}"
  if [[ -n "$message" ]] ; then
    echo "Error on or near line ${parent_lineno}: ${message}; exiting with status ${code}"
  else
    echo "Error on or near line ${parent_lineno}; exiting with status ${code}"
  fi
  exit "${code}"
}
trap 'error ${LINENO}' ERR

...임시 파일을 작성할 때마다 다음 작업을 수행합니다.

temp_foo="$(mktemp -t foobar.XXXXXX)"
tempfiles+=( "$temp_foo" )

★★★★★★★★★★★★★★★★★」$temp_foo종료시에 삭제되어 현재의 회선 번호가 인쇄됩니다.(set -e마찬가지로 에러에 의한 종료 동작도 발생합니다만, 중대한 경고가 있어 코드의 예측 가능성과 이식성이 저하됩니다).

이 「」를 하도록 할 도 있습니다.error사용자(이 경우 기본 종료 코드 1을 사용하고 메시지 없음) 또는 사용자 자신이 호출하여 명시적 값을 지정합니다.하다

error ${LINENO} "the foobar failed" 2

상태 2로 종료하고 명시적인 메시지를 표시합니다.

★★★shopt -s extdebug첫이 아닌 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "set -e"CHANGE: "KHANGE: "CHANGE" (영어)

error() {
  local last_exit_status="$?"
  local parent_lineno="$1"
  local message="${2:-(no message ($last_exit_status))}"
  local code="${3:-$last_exit_status}"
  # ... continue as above
}
trap 'error ${LINENO}' ERR
shopt -s extdebug

이것도 '어울리다'와 '가.set -eu.

그거 좋은 해결책이야.덧붙이고 싶었어요

set -e

기본적인 오류 메커니즘으로 사용됩니다.간단한 명령어가 실패하면 스크립트가 즉시 중지됩니다.이러한 오류는 거의 항상 예기치 않은 것을 나타내기 때문에 다음 명령을 계속 실행하는 것은 그다지 '안전'하지 않습니다.

이 페이지에 있는 모든 답을 읽고 많은 영감을 받았습니다.

힌트 하나 요: ,, 음, 음, 음, 음, 힌, 습, so, so, so, so, so, so, so.

콘텐츠lib..sh file: : lib.trap.sh

lib_name='trap'
lib_version=20121026

stderr_log="/dev/shm/stderr.log"

#
# TO BE SOURCED ONLY ONCE:
#
###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##

if test "${g_libs[$lib_name]+_}"; then
    return 0
else
    if test ${#g_libs[@]} == 0; then
        declare -A g_libs
    fi
    g_libs[$lib_name]=$lib_version
fi


#
# MAIN CODE:
#
###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##

set -o pipefail  # trace ERR through pipes
set -o errtrace  # trace ERR through 'time command' and other functions
set -o nounset   ## set -u : exit the script if you try to use an uninitialised variable
set -o errexit   ## set -e : exit the script if any statement returns a non-true return value

exec 2>"$stderr_log"


###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
#
# FUNCTION: EXIT_HANDLER
#
###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##

function exit_handler ()
{
    local error_code="$?"

    test $error_code == 0 && return;

    #
    # LOCAL VARIABLES:
    # ------------------------------------------------------------------
    #    
    local i=0
    local regex=''
    local mem=''

    local error_file=''
    local error_lineno=''
    local error_message='unknown'

    local lineno=''


    #
    # PRINT THE HEADER:
    # ------------------------------------------------------------------
    #
    # Color the output if it's an interactive terminal
    test -t 1 && tput bold; tput setf 4                                 ## red bold
    echo -e "\n(!) EXIT HANDLER:\n"


    #
    # GETTING LAST ERROR OCCURRED:
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #

    #
    # Read last file from the error log
    # ------------------------------------------------------------------
    #
    if test -f "$stderr_log"
        then
            stderr=$( tail -n 1 "$stderr_log" )
            rm "$stderr_log"
    fi

    #
    # Managing the line to extract information:
    # ------------------------------------------------------------------
    #

    if test -n "$stderr"
        then        
            # Exploding stderr on :
            mem="$IFS"
            local shrunk_stderr=$( echo "$stderr" | sed 's/\: /\:/g' )
            IFS=':'
            local stderr_parts=( $shrunk_stderr )
            IFS="$mem"

            # Storing information on the error
            error_file="${stderr_parts[0]}"
            error_lineno="${stderr_parts[1]}"
            error_message=""

            for (( i = 3; i <= ${#stderr_parts[@]}; i++ ))
                do
                    error_message="$error_message "${stderr_parts[$i-1]}": "
            done

            # Removing last ':' (colon character)
            error_message="${error_message%:*}"

            # Trim
            error_message="$( echo "$error_message" | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//' )"
    fi

    #
    # GETTING BACKTRACE:
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
    _backtrace=$( backtrace 2 )


    #
    # MANAGING THE OUTPUT:
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #

    local lineno=""
    regex='^([a-z]{1,}) ([0-9]{1,})$'

    if [[ $error_lineno =~ $regex ]]

        # The error line was found on the log
        # (e.g. type 'ff' without quotes wherever)
        # --------------------------------------------------------------
        then
            local row="${BASH_REMATCH[1]}"
            lineno="${BASH_REMATCH[2]}"

            echo -e "FILE:\t\t${error_file}"
            echo -e "${row^^}:\t\t${lineno}\n"

            echo -e "ERROR CODE:\t${error_code}"             
            test -t 1 && tput setf 6                                    ## white yellow
            echo -e "ERROR MESSAGE:\n$error_message"


        else
            regex="^${error_file}\$|^${error_file}\s+|\s+${error_file}\s+|\s+${error_file}\$"
            if [[ "$_backtrace" =~ $regex ]]

                # The file was found on the log but not the error line
                # (could not reproduce this case so far)
                # ------------------------------------------------------
                then
                    echo -e "FILE:\t\t$error_file"
                    echo -e "ROW:\t\tunknown\n"

                    echo -e "ERROR CODE:\t${error_code}"
                    test -t 1 && tput setf 6                            ## white yellow
                    echo -e "ERROR MESSAGE:\n${stderr}"

                # Neither the error line nor the error file was found on the log
                # (e.g. type 'cp ffd fdf' without quotes wherever)
                # ------------------------------------------------------
                else
                    #
                    # The error file is the first on backtrace list:

                    # Exploding backtrace on newlines
                    mem=$IFS
                    IFS='
                    '
                    #
                    # Substring: I keep only the carriage return
                    # (others needed only for tabbing purpose)
                    IFS=${IFS:0:1}
                    local lines=( $_backtrace )

                    IFS=$mem

                    error_file=""

                    if test -n "${lines[1]}"
                        then
                            array=( ${lines[1]} )

                            for (( i=2; i<${#array[@]}; i++ ))
                                do
                                    error_file="$error_file ${array[$i]}"
                            done

                            # Trim
                            error_file="$( echo "$error_file" | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//' )"
                    fi

                    echo -e "FILE:\t\t$error_file"
                    echo -e "ROW:\t\tunknown\n"

                    echo -e "ERROR CODE:\t${error_code}"
                    test -t 1 && tput setf 6                            ## white yellow
                    if test -n "${stderr}"
                        then
                            echo -e "ERROR MESSAGE:\n${stderr}"
                        else
                            echo -e "ERROR MESSAGE:\n${error_message}"
                    fi
            fi
    fi

    #
    # PRINTING THE BACKTRACE:
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #

    test -t 1 && tput setf 7                                            ## white bold
    echo -e "\n$_backtrace\n"

    #
    # EXITING:
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #

    test -t 1 && tput setf 4                                            ## red bold
    echo "Exiting!"

    test -t 1 && tput sgr0 # Reset terminal

    exit "$error_code"
}
trap exit_handler EXIT                                                  # ! ! ! TRAP EXIT ! ! !
trap exit ERR                                                           # ! ! ! TRAP ERR ! ! !


###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
#
# FUNCTION: BACKTRACE
#
###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##

function backtrace
{
    local _start_from_=0

    local params=( "$@" )
    if (( "${#params[@]}" >= "1" ))
        then
            _start_from_="$1"
    fi

    local i=0
    local first=false
    while caller $i > /dev/null
    do
        if test -n "$_start_from_" && (( "$i" + 1   >= "$_start_from_" ))
            then
                if test "$first" == false
                    then
                        echo "BACKTRACE IS:"
                        first=true
                fi
                caller $i
        fi
        let "i=i+1"
    done
}

return 0



「 」:
트랩 테스트:

#!/bin/bash

source 'lib.trap.sh'

echo "doing something wrong now .."
echo "$foo"

exit 0


「 」:

bash trap-test.sh

출력:

doing something wrong now ..

(!) EXIT HANDLER:

FILE:       trap-test.sh
LINE:       6

ERROR CODE: 1
ERROR MESSAGE:
foo:   unassigned variable

BACKTRACE IS:
1 main trap-test.sh

Exiting!


아래 스크린샷에서 알 수 있듯이 출력은 컬러로 표시되고 에러 메시지는 사용 언어로 표시됩니다.

여기에 이미지 설명 입력

"set -e"와 동등한 대안은 다음과 같습니다.

set -o errexit

단순한 "-e"보다 깃발의 의미를 어느 정도 명확하게 한다.

임의 추가: 플래그를 일시적으로 비활성화하고 기본 설정(종료 코드에 관계없이 계속 실행)으로 되돌리려면 다음 명령을 사용하십시오.

set +e
echo "commands run here returning non-zero exit codes will not cause the entire script to fail"
echo "false returns 1 as an exit code"
false
set -e

이것에 의해, 다른 응답에 기재되어 있는 적절한 에러 처리는 할 수 없지만, (bash와 같이) 빠르고 효과적입니다.

여기에 제시된 아이디어에서 영감을 얻어 는 bash 보일러 플레이트 프로젝트에서 bash 스크립트의 오류를 다루기 위한 읽기 쉽고 편리한 방법을 개발했습니다.

소싱하는 을 사용할 수 에러 , 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」를 사용하고 있는 것처럼, 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」를 사용합니다.set -etrapERR그리고 bash-fu) :

bash-oo-param 오류 처리

try and catch 또는 throw 키워드 등 오류를 처리하는 데 도움이 되는 추가 기능이 있습니다.이 기능을 사용하면 역추적을 확인하기 위해 한 시점에서 실행을 중단할 수 있습니다.또, 단말기가 서포트하고 있는 경우는, 전원선 이모티콘을 출력해, 출력의 일부를 색칠해 읽기 쉽게 해, 코드행의 문맥에서 예외를 발생시킨 방법에 밑줄을 그습니다.

단점은 - portable은 아니지만 - 코드는 bash로 동작합니다.아마 > = 4 정도일 것입니다(그러나 bash 3을 위해 약간의 노력을 기울이면 port 할 수 있다고 생각합니다).

코드를 여러 파일로 나누어 취급을 개선하지만, 위의 답변에서 얻은 역추적 아이디어에 영감을 받았습니다.

자세한 내용을 읽거나 소스를 보려면 GitHub:

https://github.com/niieani/bash-oo-framework#error-handling-with-exceptions-and-throw

나는 정말 부르기 쉬운 것이 더 좋다.그래서 저는 조금 복잡해 보이지만 사용하기 쉬운 것을 사용합니다.보통 아래 코드를 복사하여 스크립트에 붙여넣기만 하면 됩니다.코드 뒤에 설명이 있습니다.

#This function is used to cleanly exit any script. It does this displaying a
# given error message, and exiting with an error code.
function error_exit {
    echo
    echo "$@"
    exit 1
}
#Trap the killer signals so that we can exit with a good message.
trap "error_exit 'Received signal SIGHUP'" SIGHUP
trap "error_exit 'Received signal SIGINT'" SIGINT
trap "error_exit 'Received signal SIGTERM'" SIGTERM

#Alias the function so that it will print a message with the following format:
#prog-name(@line#): message
#We have to explicitly allow aliases, we do this because they make calling the
#function much easier (see example).
shopt -s expand_aliases
alias die='error_exit "Error ${0}(@`echo $(( $LINENO - 1 ))`):"'

보통 error_exit 함수 옆에 있는 청소 함수를 호출하는데 스크립트마다 다르기 때문에 생략했습니다.트랩은 일반적인 종단 신호를 포착하여 모든 것이 정리되었는지 확인합니다.진짜 마법은 가명이죠나는 모든 것이 실패했는지 확인하는 것을 좋아한다.그래서 보통 저는 "if!" 라고 하는 문장으로 프로그램을 호출합니다.에일리어스는 회선번호에서 1을 빼면 장애가 발생한 위치를 알 수 있습니다.전화하는 것도 아주 간단하고 바보같은 증거죠다음은 예를 제시하겠습니다( /bin/false를 호출하는 것으로 대체하십시오).

#This is an example useage, it will print out
#Error prog-name (@1): Who knew false is false.
if ! /bin/false ; then
    die "Who knew false is false."
fi

또 하나의 고려사항은 반환되는 종료 코드입니다.1bash 자체에서 사용하는 소수의 예약된 종료 코드가 있지만 C/C++ 표준에 준거하려면 사용자 정의 코드가 64~113 범위에 있어야 한다고 같은 페이지에서 주장하고 있습니다.

, 비트 를 「비트의 벡터 어프로치」라고 것도 고려할 수 .mount코드의 : uses uses uses 、 uses uses uses uses uses uses uses 。

 0  success
 1  incorrect invocation or permissions
 2  system error (out of memory, cannot fork, no more loop devices)
 4  internal mount bug or missing nfs support in mount
 8  user interrupt
16  problems writing or locking /etc/mtab
32  mount failure
64  some mount succeeded

OR코드를 함께 입력하면 스크립트가 여러 오류를 동시에 알릴 수 있습니다.

다음 트랩 코드를 사용합니다. 파이프'시간' 명령을 통해 오류를 추적할 수도 있습니다.

#!/bin/bash
set -o pipefail  # trace ERR through pipes
set -o errtrace  # trace ERR through 'time command' and other functions
function error() {
    JOB="$0"              # job name
    LASTLINE="$1"         # line of error occurrence
    LASTERR="$2"          # error code
    echo "ERROR in ${JOB} : line ${LASTLINE} with exit code ${LASTERR}"
    exit 1
}
trap 'error ${LINENO} ${?}' ERR

사용한 적이 있다

die() {
        echo $1
        kill $$
}

그전에는 어떤 이유에서인지 '무결함'이 실패했기 때문인 것 같아요.단, 위의 디폴트는 좋은 생각인 것 같습니다.

이것은 한동안 나에게 도움이 되었다.오류 또는 경고 메시지를 파라미터별로 한 줄씩 빨간색으로 출력하며 옵션 종료 코드를 사용할 수 있습니다.

# Custom errors
EX_UNKNOWN=1

warning()
{
    # Output warning messages
    # Color the output red if it's an interactive terminal
    # @param $1...: Messages

    test -t 1 && tput setf 4

    printf '%s\n' "$@" >&2

    test -t 1 && tput sgr0 # Reset terminal
    true
}

error()
{
    # Output error messages with optional exit code
    # @param $1...: Messages
    # @param $N: Exit code (optional)

    messages=( "$@" )

    # If the last parameter is a number, it's not part of the messages
    last_parameter="${messages[@]: -1}"
    if [[ "$last_parameter" =~ ^[0-9]*$ ]]
    then
        exit_code=$last_parameter
        unset messages[$((${#messages[@]} - 1))]
    fi

    warning "${messages[@]}"

    exit ${exit_code:-$EX_UNKNOWN}
}

이것이 도움이 될지는 모르겠지만, 오류 체크(이전 명령어 종료 코드)를 포함하기 위해 여기서 제안하는 기능 중 일부를 수정했습니다.또한 각 "체크"에서 오류의 의미를 나타내는 "메시지"를 매개 변수로 전달합니다.

#!/bin/bash

error_exit()
{
    if [ "$?" != "0" ]; then
        log.sh "$1"
        exit 1
    fi
}

다른 스크립트(사용하는 에는 다른 ) 로합니다.export -f error_exit함수의 이름을 쓰고 다음과 같이 메시지를 매개 변수로 전달합니다.

#!/bin/bash

cd /home/myuser/afolder
error_exit "Unable to switch to folder"

rm *
error_exit "Unable to delete all files"

하여 자동 수 시 이됩니다(Bash 파일).이치노log.sh★★★★★★★★★★★★★★★★★★★★★)

이 트릭은 명령 또는 함수가 누락된 경우에 유용합니다.누락된 함수(또는 실행 파일)의 이름은 $_로 전달됩니다.

function handle_error {
    status=$?
    last_call=$1

    # 127 is 'command not found'
    (( status != 127 )) && return

    echo "you tried to call $last_call"
    return
}

# Trap errors.
trap 'handle_error "$_"' ERR

이 기능은 최근 상당히 유용하게 사용되고 있습니다.

action () {
    # Test if the first parameter is non-zero
    # and return straight away if so
    if test $1 -ne 0
    then
        return $1
    fi

    # Discard the control parameter
    # and execute the rest
    shift 1
    "$@"
    local status=$?

    # Test the exit status of the command run
    # and display an error message on failure
    if test ${status} -ne 0
    then
        echo Command \""$@"\" failed >&2
    fi

    return ${status}
}

실행할 명령어 이름에 0 또는 마지막 반환 값을 추가하여 호출하므로 오류 값을 확인할 필요 없이 명령어를 체인할 수 있습니다.이를 통해 다음 문 블록이 생성됩니다.

command1 param1 param2 param3...
command2 param1 param2 param3...
command3 param1 param2 param3...
command4 param1 param2 param3...
command5 param1 param2 param3...
command6 param1 param2 param3...

다음과 같이 됩니다.

action 0 command1 param1 param2 param3...
action $? command2 param1 param2 param3...
action $? command3 param1 param2 param3...
action $? command4 param1 param2 param3...
action $? command5 param1 param2 param3...
action $? command6 param1 param2 param3...

<<<Error-handling code here>>>

명령어 중 하나가 실패하면 오류 코드가 블록 끝에 전달됩니다.이전 명령어가 실패했을 때 후속 명령어를 실행하고 싶지 않지만 스크립트가 즉시 종료되는 것(루프 내 등)도 원하지 않을 때 유용합니다.

가끔씩set -e,trap ERR,set -o ,set -o pipefail그리고.set -o errtrace셸에 자동 에러 검출을 추가하려고 하기 때문에, 정상적으로 동작하지 않습니다.이것은 실제로는 잘 되지 않는다.

제가 봤을 때, 제가 보기엔set -e에러 체크 코드를 직접 작성해야 합니다.사용하는 것이 현명하다면set -e잠재적인 고차(gotcha)에 주의해 주세요.

중하려면 , 「」를 합니다.exec 1>/dev/null ★★★★★★★★★★★★★★★★★」exec 2>/dev/null
/dev/nullLinux linux 、 null 。 명령어 할 수 .

★★★의 try/catch 하면 .&& ★★★★★★★★★★★★★★★★★」||하려면 & & 를 & & 를 합니다.

{ # try

    command &&
    # your command 

} || { 
    # catch exception 
}

이렇게 쓸 수도 요.if else:

if [[ Condition ]]; then
    # if true
else
    # if false
fi

$?1 의 show output 1 을 반환합니다.

트랩을 사용하는 것이 항상 가능한 것은 아닙니다.예를 들어, 오류 처리가 필요하고 어떤 스크립트에서도 호출할 수 있는 재사용 가능한 함수를 쓰고 있는 경우(도움말 함수로 파일을 소싱한 후), 이 함수는 외부 스크립트의 종료 시간을 가정할 수 없기 때문에 트랩을 사용하는 것이 매우 어렵습니다.트랩을 사용할 때의 또 다른 단점은 컴포넌트 불량입니다.발신자 체인으로 이전에 설정되어 있던 트랩을 덮어쓸 위험이 있기 때문입니다.

트랩 없이 적절한 에러 처리를 실시하기 위해서 사용할 수 있는 작은 트릭이 있습니다.에서도 알 수, 른른른 as as as as as as as as as as as as as as as as as as as as 。set -e doesn doesn 、 [ ] 、 [ ]를 사용하면 동작하지 않습니다.||예를 들어, 하위 쉘로 실행해도 연산자가 작동하지 않습니다.

#!/bin/sh

# prints:
#
# --> outer
# --> inner
# ./so_1.sh: line 16: some_failed_command: command not found
# <-- inner
# <-- outer

set -e

outer() {
  echo '--> outer'
  (inner) || {
    exit_code=$?
    echo '--> cleanup'
    return $exit_code
  }
  echo '<-- outer'
}

inner() {
  set -e
  echo '--> inner'
  some_failed_command
  echo '<-- inner'
}

outer

★★★★★★★★★★★★★★★★★.||작업자는 청소 전에 외부 기능에서 복귀하는 것을 방지하기 위해 필요합니다.요령은 inner 명령어를 백그라운드에서 실행한 후 즉시 대기하는 것입니다.wait하고, builtin을 사용하고 .★★★★★★★★★★★★★★★★★★,|| 후에wait함수가 에, 「 」, 「 」, 「 」를 참조해 주세요set -e는 후자의 내부에서 올바르게 동작합니다.

#!/bin/sh

# prints:
#
# --> outer
# --> inner
# ./so_2.sh: line 27: some_failed_command: command not found
# --> cleanup

set -e

outer() {
  echo '--> outer'
  inner &
  wait $! || {
    exit_code=$?
    echo '--> cleanup'
    return $exit_code
  }
  echo '<-- outer'
}

inner() {
  set -e
  echo '--> inner'
  some_failed_command
  echo '<-- inner'
}

outer

여기 이 아이디어를 기반으로 하는 일반적인 기능이 있습니다.POSIX 를 하면, POSIX 합니다.localall 'sublish' 및 'sublish'를 모두 .local x=yx=y:

# [CLEANUP=cleanup_cmd] run cmd [args...]
#
# `cmd` and `args...` A command to run and its arguments.
#
# `cleanup_cmd` A command that is called after cmd has exited,
# and gets passed the same arguments as cmd. Additionally, the
# following environment variables are available to that command:
#
# - `RUN_CMD` contains the `cmd` that was passed to `run`;
# - `RUN_EXIT_CODE` contains the exit code of the command.
#
# If `cleanup_cmd` is set, `run` will return the exit code of that
# command. Otherwise, it will return the exit code of `cmd`.
#
run() {
  local cmd="$1"; shift
  local exit_code=0

  local e_was_set=1; if ! is_shell_attribute_set e; then
    set -e
    e_was_set=0
  fi

  "$cmd" "$@" &

  wait $! || {
    exit_code=$?
  }

  if [ "$e_was_set" = 0 ] && is_shell_attribute_set e; then
    set +e
  fi

  if [ -n "$CLEANUP" ]; then
    RUN_CMD="$cmd" RUN_EXIT_CODE="$exit_code" "$CLEANUP" "$@"
    return $?
  fi

  return $exit_code
}


is_shell_attribute_set() { # attribute, like "x"
  case "$-" in
    *"$1"*) return 0 ;;
    *)    return 1 ;;
  esac
}

사용 예:

#!/bin/sh
set -e

# Source the file with the definition of `run` (previous code snippet).
# Alternatively, you may paste that code directly here and comment the next line.
. ./utils.sh


main() {
  echo "--> main: $@"
  CLEANUP=cleanup run inner "$@"
  echo "<-- main"
}


inner() {
  echo "--> inner: $@"
  sleep 0.5; if [ "$1" = 'fail' ]; then
    oh_my_god_look_at_this
  fi
  echo "<-- inner"
}


cleanup() {
  echo "--> cleanup: $@"
  echo "    RUN_CMD = '$RUN_CMD'"
  echo "    RUN_EXIT_CODE = $RUN_EXIT_CODE"
  sleep 0.3
  echo '<-- cleanup'
  return $RUN_EXIT_CODE
}

main "$@"

예제를 실행합니다.

$ ./so_3 fail; echo "exit code: $?"

--> main: fail
--> inner: fail
./so_3: line 15: oh_my_god_look_at_this: command not found
--> cleanup: fail
    RUN_CMD = 'inner'
    RUN_EXIT_CODE = 127
<-- cleanup
exit code: 127

$ ./so_3 pass; echo "exit code: $?"

--> main: pass
--> inner: pass
<-- inner
--> cleanup: pass
    RUN_CMD = 'inner'
    RUN_EXIT_CODE = 0
<-- cleanup
<-- main
exit code: 0

할 때 해야 할 에에서 셸 변경이 입니다.run명령어가 서브셸에서 실행되기 때문에는 호출 함수에 전파되지 않습니다.

언급URL : https://stackoverflow.com/questions/64786/error-handling-in-bash

반응형