it-swarm-ko.tech

Bash에서 오류 처리

Bash에서 오류를 처리하는 가장 좋아하는 방법은 무엇입니까? 웹에서 찾은 오류를 처리하는 가장 좋은 예는 William Shotts, Jr의 http://www.linuxcommand.org 에 의해 작성되었습니다.

그는 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 스크립트에서 사용하는 더 나은 오류 처리 루틴이 있습니까?

211
Noob

함정을 사용하십시오!

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로 종료되고 명시적인 메시지가 표시됩니다.

145
Charles Duffy

좋은 해결책입니다. 방금 추가하고 싶었습니다

set -e

기본적인 오류 메커니즘으로. 간단한 명령이 실패하면 스크립트가 즉시 중지됩니다. 나는 이것이 기본 동작이어야한다고 생각합니다. 그러한 오류는 거의 항상 예기치 않은 것을 의미하기 때문에 다음 명령을 계속 실행하는 것이 실제로 '정치적인'것은 아닙니다.

112
Bruno De Fraine

이 페이지의 모든 답변을 읽으면 많은 영감을 얻었습니다.

그래서 내 힌트는 다음과 같습니다.

파일 내용 : 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=( "[email protected]" )
    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



사용 예 :
파일 내용 : trap-test.sh

#!/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!


아래 스크린 샷에서 볼 수 있듯이 출력은 색상으로 표시되고 오류 메시지는 사용 된 언어로 제공됩니다.

enter image description here

71
Luca Borrione

"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와 마찬가지로).

22
Ben Scholbrock

여기에 제시된 아이디어에서 영감을 얻어 bash 상용구 프로젝트 에서 bash 스크립트의 오류를 처리 할 수있는 읽기 쉽고 편리한 방법을 개발했습니다.

라이브러리를 소싱하면 다음과 같은 결과를 즉시 얻을 수 있습니다. 즉, trapERR 및 일부 덕분에 set -e를 사용하는 것처럼 오류가 발생하면 실행이 중지됩니다. bash-fu) :

bash-oo-framework error handling

try and catch 또는 throw 키워드와 같이 오류를 처리하는 데 도움이되는 몇 가지 추가 기능이 있습니다. 또한 단말기가 지원하는 경우 전력선 이모지를 뱉어 내고 출력의 일부를 색칠하여 가독성을 높이고 코드 라인의 맥락에서 예외를 일으킨 방법에 밑줄을 긋습니다.

단점은-이식성이 없습니다-코드는 bash에서 작동하며 아마도 = 4 이상 일 것입니다 (그러나 3을 bash하기 위해 약간의 노력으로 이식 될 수 있다고 상상할 것입니다).

코드는 더 나은 처리를 위해 여러 파일로 분리되어 있지만 Luca Borrione의 위 답변 의 역 추적 아이디어에서 영감을 얻었습니다.

자세한 내용을 읽거나 소스를 살펴 보려면 GitHub를 참조하십시오.

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

19
niieani

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

#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 "[email protected]"
    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
11
Michael Nooner

또 다른 고려 사항은 리턴 할 종료 코드입니다. 소수의 bash 자체가 사용하는 예약 된 종료 코드 가 있지만 동일한 페이지에서 사용자 정의 코드가 64-113 범위에 있어야한다고 주장하지만 "1"는 꽤 표준입니다. C/C++ 표준.

mount이 종료 코드에 사용하는 비트 벡터 접근 방식을 고려할 수도 있습니다.

 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- 코드를 함께 사용하면 스크립트에서 여러 개의 동시 오류를 알릴 수 있습니다.

6
yukondude

다음 트랩 코드를 사용하면 파이프 및 '시간'명령을 통해 오류를 추적 할 수 있음

#!/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
4
Olivier Delrieu

나는 사용했다

die() {
        echo $1
        kill $$
}

전에; 나는 '종료'가 어떤 이유로 나를 위해 실패했기 때문에 생각합니다. 위의 기본값은 좋은 생각처럼 보입니다.

3
pjz

This 한동안 나를 위해 잘 봉사했습니다. 매개 변수 당 한 줄씩 오류 또는 경고 메시지를 빨간색으로 인쇄하고 선택적인 종료 코드를 허용합니다.

# 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' "[email protected]" >&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=( "[email protected]" )

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

이것이 당신에게 도움이되는지 확실하지 않지만, 오류 확인 (이전 명령의 종료 코드)을 포함시키기 위해 여기에 제안 된 기능 중 일부를 수정했습니다. 각 "체크"에서 로깅 목적으로 오류가 무엇인지 "메시지"를 매개 변수로 전달합니다.

#!/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가 그렇게합니다)

2
Nelson Rodriguez

이 기능은 최근에 저에게 잘 제공되었습니다

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
    "[email protected]"
    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 \""[email protected]"\" 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>>>

명령 중 하나라도 실패하면 오류 코드가 단순히 블록의 끝으로 전달됩니다. 이전 명령이 실패한 경우 후속 명령을 실행하지 않으려는 경우에도 유용하지만 스크립트가 바로 종료되는 것을 원하지 않습니다 (예 : 루프 내부).

1
xarxziux

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

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
0
Orwellophile

트랩을 사용하는 것이 항상 옵션은 아닙니다. 예를 들어, 오류 처리가 필요하고 스크립트에서 호출 할 수있는 일종의 재사용 가능한 함수를 작성하는 경우 (도우미 함수로 파일을 소싱 한 후), 해당 함수는 외부 스크립트의 종료 시간에 대해 아무 것도 가정 할 수 없습니다. 함정 사용이 매우 어렵습니다. 트랩 사용의 또 다른 단점은 호출자 체인에서 이전에 설정되었을 수있는 이전 트랩을 덮어 쓸 위험이 있으므로 구성 성이 나쁘다는 것입니다.

트랩없이 적절한 오류 처리를 수행하는 데 사용할 수있는 약간의 트릭이 있습니다. 이미 다른 답변에서 알 수 있듯이 set -e는 하위 쉘에서 실행하더라도 || 연산자를 사용하면 명령 내에서 작동하지 않습니다. 예를 들어, 이것은 작동하지 않습니다.

#!/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

그러나 정리하기 전에 외부 함수에서 반환되는 것을 막으려면 || 연산자가 필요합니다. 속임수는 내부 명령을 백그라운드에서 실행 한 다음 즉시 기다립니다. wait 내장은 내부 명령의 종료 코드를 반환하며 이제 내부 함수가 아닌 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

이 아이디어를 기반으로하는 일반적인 기능은 다음과 같습니다. local 키워드를 제거하면 모든 POSIX 호환 쉘에서 작동해야합니다. 즉, 모든 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" "[email protected]" &

  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" "[email protected]"
    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: [email protected]"
  CLEANUP=cleanup run inner "[email protected]"
  echo "<-- main"
}


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


cleanup() {
  echo "--> cleanup: [email protected]"
  echo "    RUN_CMD = '$RUN_CMD'"
  echo "    RUN_EXIT_CODE = $RUN_EXIT_CODE"
  sleep 0.3
  echo '<-- cleanup'
  return $RUN_EXIT_CODE
}

main "[email protected]"

예제 실행 :

$ ./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에 전달한 명령에서 수행 된 쉘 변수의 모든 수정 사항이 호출 함수로 전파되지 않는다는 것입니다.

0
skozin