Có lệnh TRY CATCH trong Bash không


347

Tôi đang viết một tập lệnh shell và cần kiểm tra xem ứng dụng đầu cuối đã được cài đặt chưa. Tôi muốn sử dụng lệnh TRY / CATCH để làm điều này trừ khi có cách gọn gàng hơn.


1
Nó có thể hữu ích nếu bạn có thể giải thích vấn đề mà bạn đang cố gắng giải quyết. Có vẻ như bạn không hoàn toàn mới ở đây, nhưng bạn vẫn có thể muốn truy cập Trung tâm trợ giúp và xem trợ giúp về cách đặt câu hỏi hay.
devnull

Điều đó nói rằng, có vẻ như nói rằng help testcó thể giúp bạn tìm ra giải pháp cho vấn đề của bạn.
devnull

2
thử / bắt / cuối cùng khối không phải là một lệnh, đó là một cấu trúc
Ben


@LeeProbert: Vì bạn không có ngoại lệ trong bash, tôi tự hỏi bạn đang muốn bắt cái gì. Thứ gần nhất đi theo hướng của một ngoại lệ sẽ là một tín hiệu và hầu hết (không phải tất cả) chúng có thể bắt được bằng cách sử dụng traplệnh.
dùng1934428

Câu trả lời:


562

Có một lệnh TRY CATCH trong Bash?

Không.

Bash không có nhiều thứ xa xỉ như người ta có thể tìm thấy trong nhiều ngôn ngữ lập trình.

Không có try/catchtrong bash; tuy nhiên, người ta có thể đạt được hành vi tương tự bằng cách sử dụng &&hoặc ||.

Sử dụng ||:

nếu command1thất bại thì command2chạy như sau

command1 || command2

Tương tự, sử dụng &&, command2sẽ chạy nếu command1thành công

Giá trị gần đúng nhất try/catchnhư sau

{ # try

    command1 &&
    #save your output

} || { # catch
    # save log for exception 
}

Ngoài ra bash cũng chứa một số cơ chế xử lý lỗi

set -e

nó dừng tập lệnh của bạn nếu bất kỳ lệnh đơn giản nào thất bại.

Và cũng tại sao không if...else. Đó là người bạn tốt nhất của bạn.


18
Với điều này, bạn cần lưu ý rằng mã #save your outputkhông bị lỗi hoặc khối "bắt" vẫn sẽ thực thi.
chepner

7
Có một gợi ý để sử dụng một if...elsecấu trúc. Điều này có nghĩa là các lệnh bash giải quyết là "trung thực" nếu chúng chạy thành công và "giả" nếu chúng thất bại?
Luke Griffiths

6
Đối với độc giả của chủ đề này: dường như đó set -ekhông nhất thiết là cách tốt nhất để làm mọi việc; Dưới
Luke Davis

2
Tôi có thể biết làm thế nào để đăng nhập ngoại lệ? Thông thường trong mã java, chúng ta có thể sử dụng system.out.log (e), nhưng trong shell thì sao?
Panadol Chong

112

Dựa trên một số câu trả lời tôi tìm thấy ở đây, tôi đã tạo cho mình một tệp trợ giúp nhỏ để lấy nguồn cho các dự án của mình:

trycatch.sh

#!/bin/bash

function try()
{
    [[ $- = *e* ]]; SAVED_OPT_E=$?
    set +e
}

function throw()
{
    exit $1
}

function catch()
{
    export ex_code=$?
    (( $SAVED_OPT_E )) && set +e
    return $ex_code
}

function throwErrors()
{
    set -e
}

function ignoreErrors()
{
    set +e
}

đây là một ví dụ trông như thế nào khi sử dụng:

#!/bin/bash
export AnException=100
export AnotherException=101

# start with a try
try
(   # open a subshell !!!
    echo "do something"
    [ someErrorCondition ] && throw $AnException

    echo "do something more"
    executeCommandThatMightFail || throw $AnotherException

    throwErrors # automaticatly end the try block, if command-result is non-null
    echo "now on to something completely different"
    executeCommandThatMightFail

    echo "it's a wonder we came so far"
    executeCommandThatFailsForSure || true # ignore a single failing command

    ignoreErrors # ignore failures of commands until further notice
    executeCommand1ThatFailsForSure
    local result = $(executeCommand2ThatFailsForSure)
    [ result != "expected error" ] && throw $AnException # ok, if it's not an expected error, we want to bail out!
    executeCommand3ThatFailsForSure

    echo "finished"
)
# directly after closing the subshell you need to connect a group to the catch using ||
catch || {
    # now you can handle
    case $ex_code in
        $AnException)
            echo "AnException was thrown"
        ;;
        $AnotherException)
            echo "AnotherException was thrown"
        ;;
        *)
            echo "An unexpected exception was thrown"
            throw $ex_code # you can rethrow the "exception" causing the script to exit if not caught
        ;;
    esac
}

2
Bạn có thể làm thế nào để bạn nhập các hàm thử bắt vào ví dụ khác không? (Tôi giả sử chúng ở trong các tệp riêng biệt)
kilianc

1
@kilianc: Tôi chỉ lấy nguồn như: nguồn inc / trycatch.sh.
Mathias Henze

2
@MathiasHenze Cảm ơn người đàn ông, mã của bạn rất tuyệt. Nhưng tại sao bạn cần một ||sau catchvà trước {}khối? Tôi đã có thể nghĩ nó là một&&
Remy San

(câu trả lời muộn cho bất kỳ ai tìm thấy điều này) Về cơ bản, trường hợp lỗi if False or run_if_failed()có nghĩa là ngắn mạch HOẶC đã thử câu lệnh đầu tiên không trả về đúng và hiện đang chuyển sang câu lệnh tiếp theo. &&sẽ không hoạt động vì câu lệnh đầu tiên ( try) mang lại sai, có nghĩa là catchcâu lệnh không cần thiết theo quy tắc tautology false&any equals false. Chỉ một mạch không ngắn VÀ / HOẶC sẽ thực thi cả hai.
ldmtwo

69

Tôi đã phát triển một triển khai thử và bắt gần như hoàn hảo trong bash, cho phép bạn viết mã như:

try 
    echo 'Hello'
    false
    echo 'This will not be displayed'

catch 
    echo "Error in $__EXCEPTION_SOURCE__ at line: $__EXCEPTION_LINE__!"

Bạn thậm chí có thể lồng các khối thử bắt bên trong chính chúng!

try {
    echo 'Hello'

    try {
        echo 'Nested Hello'
        false
        echo 'This will not execute'
    } catch {
        echo "Nested Caught (@ $__EXCEPTION_LINE__)"
    }

    false
    echo 'This will not execute too'

} catch {
    echo "Error in $__EXCEPTION_SOURCE__ at line: $__EXCEPTION_LINE__!"
}

Mã này là một phần của khung / bash bash của tôi . Nó tiếp tục mở rộng ý tưởng thử và bắt với những thứ như xử lý lỗi với backtrace và ngoại lệ (cộng với một số tính năng hay khác).

Đây là mã chịu trách nhiệm chỉ để thử và bắt:

set -o pipefail
shopt -s expand_aliases
declare -ig __oo__insideTryCatch=0

# if try-catch is nested, then set +e before so the parent handler doesn't catch us
alias try="[[ \$__oo__insideTryCatch -gt 0 ]] && set +e;
           __oo__insideTryCatch+=1; ( set -e;
           trap \"Exception.Capture \${LINENO}; \" ERR;"
alias catch=" ); Exception.Extract \$? || "

Exception.Capture() {
    local script="${BASH_SOURCE[1]#./}"

    if [[ ! -f /tmp/stored_exception_source ]]; then
        echo "$script" > /tmp/stored_exception_source
    fi
    if [[ ! -f /tmp/stored_exception_line ]]; then
        echo "$1" > /tmp/stored_exception_line
    fi
    return 0
}

Exception.Extract() {
    if [[ $__oo__insideTryCatch -gt 1 ]]
    then
        set -e
    fi

    __oo__insideTryCatch+=-1

    __EXCEPTION_CATCH__=( $(Exception.GetLastException) )

    local retVal=$1
    if [[ $retVal -gt 0 ]]
    then
        # BACKWARDS COMPATIBILE WAY:
        # export __EXCEPTION_SOURCE__="${__EXCEPTION_CATCH__[(${#__EXCEPTION_CATCH__[@]}-1)]}"
        # export __EXCEPTION_LINE__="${__EXCEPTION_CATCH__[(${#__EXCEPTION_CATCH__[@]}-2)]}"
        export __EXCEPTION_SOURCE__="${__EXCEPTION_CATCH__[-1]}"
        export __EXCEPTION_LINE__="${__EXCEPTION_CATCH__[-2]}"
        export __EXCEPTION__="${__EXCEPTION_CATCH__[@]:0:(${#__EXCEPTION_CATCH__[@]} - 2)}"
        return 1 # so that we may continue with a "catch"
    fi
}

Exception.GetLastException() {
    if [[ -f /tmp/stored_exception ]] && [[ -f /tmp/stored_exception_line ]] && [[ -f /tmp/stored_exception_source ]]
    then
        cat /tmp/stored_exception
        cat /tmp/stored_exception_line
        cat /tmp/stored_exception_source
    else
        echo -e " \n${BASH_LINENO[1]}\n${BASH_SOURCE[2]#./}"
    fi

    rm -f /tmp/stored_exception /tmp/stored_exception_line /tmp/stored_exception_source
    return 0
}

Hãy sử dụng, fork và đóng góp - đó là trên GitHub .


@ erm3nda Vui mừng khi nghe điều đó! Tôi nghĩ rằng tôi đã giết một vài lỗi sau khi tôi đăng bài này, vì vậy hãy xem GitHub để cập nhật (bạn sẽ cần bao gồm 03_exception.sh và 04_try_catch.sh). Phiên bản hiện tại là khá nhiều chống đạn theo như tôi biết.
niieani

Rất đẹp! Tôi sẽ sử dụng trong dự án của tôi. Tôi đã đi làm trong 5 phút và số centos của tôi đã có trong bash 4.2.46
Felipe

1
Có một vấn đề cơ bản ở đây: nếu bạn thay đổi một biến trong khối thử, nó sẽ không được nhìn thấy bên ngoài vì nó đang chạy trong lớp vỏ phụ.
Kan Li

1
@KanLi đúng. Nếu bạn quan tâm đến đầu ra của thử / bắt, bạn có thể chụp nó như sau:my_output=$(try { code...; } catch { code...; })
niieani

Trong phiên bản mới nhất, có vẻ như EXCEPTION_LINE đã được đổi tên thành BACKTRACE_LINE github.com/niieani/bash-oo-framework#USE-try--catch
Ben Creasy

19

Bạn có thể sử dụng trap:

try { block A } catch { block B } finally { block C }

Dịch sang:

(
  set -Ee
  function _catch {
    block B
    exit 0  # optional; use if you don't want to propagate (rethrow) error to outer shell
  }
  function _finally {
    block C
  }
  trap _catch ERR
  trap _finally EXIT
  block A
)

Tôi cũng muốn -Ecờ tôi nghĩ, vì vậy cái bẫy truyền đến các chức năng
Mark K Cowan

16

Có rất nhiều giải pháp tương tự có thể làm việc. Dưới đây là một cách đơn giản và hiệu quả để thực hiện thử / bắt, với lời giải thích trong các bình luận.

#!/bin/bash

function a() {
  # do some stuff here
}
function b() {
  # do more stuff here
}

# this subshell is a scope of try
# try
(
  # this flag will make to exit from current subshell on any error
  # inside it (all functions run inside will also break on any error)
  set -e
  a
  b
  # do more stuff here
)
# and here we catch errors
# catch
errorCode=$?
if [ $errorCode -ne 0 ]; then
  echo "We have an error"
  # We exit the all script with the same error, if you don't want to
  # exit it and continue, just delete this line.
  exit $errorCode
fi

15

bashkhông hủy bỏ việc thực thi đang chạy trong trường hợp có gì đó phát hiện trạng thái lỗi (trừ khi bạn đặt -ecờ). Các ngôn ngữ lập trình cung cấp try/catchlàm điều này để ngăn chặn "giải cứu" vì tình huống đặc biệt này (do đó thường được gọi là "ngoại lệ").

Trong bash, thay vào đó, chỉ lệnh trong câu hỏi sẽ thoát ra với một mã lớn hơn thoát hơn 0, chỉ ra rằng tình trạng lỗi. Bạn có thể kiểm tra điều đó tất nhiên, nhưng vì không có sự bảo đảm tự động nào trong số đó, nên hãy thử / bắt không có ý nghĩa gì. Nó chỉ thiếu bối cảnh đó.

Tuy nhiên, bạn có thể mô phỏng việc cứu trợ bằng cách sử dụng các lớp vỏ phụ có thể chấm dứt tại một điểm bạn quyết định:

(
  echo "Do one thing"
  echo "Do another thing"
  if some_condition
  then
    exit 3  # <-- this is our simulated bailing out
  fi
  echo "Do yet another thing"
  echo "And do a last thing"
)   # <-- here we arrive after the simulated bailing out, and $? will be 3 (exit code)
if [ $? = 3 ]
then
  echo "Bail out detected"
fi

Thay vì điều đó some_conditionvới một ifbạn cũng có thể thử một lệnh và trong trường hợp nó không thành công (có mã thoát lớn hơn 0), hãy bảo lãnh:

(
  echo "Do one thing"
  echo "Do another thing"
  some_command || exit 3
  echo "Do yet another thing"
  echo "And do a last thing"
)
...

Thật không may, bằng cách sử dụng kỹ thuật này, bạn bị giới hạn ở 255 mã thoát khác nhau (1..255) và không có đối tượng ngoại lệ nào có thể được sử dụng.

Nếu bạn cần thêm thông tin để vượt qua ngoại lệ mô phỏng của mình, bạn có thể sử dụng thiết bị xuất chuẩn của các mạng con, nhưng điều đó hơi phức tạp và có thể là một câu hỏi khác ;-)

Sử dụng -ecờ được đề cập ở trên vào trình bao, bạn thậm chí có thể loại bỏ exittuyên bố rõ ràng đó :

(
  set -e
  echo "Do one thing"
  echo "Do another thing"
  some_command
  echo "Do yet another thing"
  echo "And do a last thing"
)
...

1
Đây thực sự nên là câu trả lời được chấp nhận vì nó là logic thử / bắt gần nhất mà bạn có thể có với shell.
Trent

13

Như mọi người nói, bash không có cú pháp thử / bắt được hỗ trợ ngôn ngữ phù hợp. Bạn có thể khởi chạy bash với -eđối số hoặc sử dụng set -ebên trong tập lệnh để hủy bỏ toàn bộ quá trình bash nếu bất kỳ lệnh nào có mã thoát khác không. (Bạn cũng có thểset +e tạm thời cho phép các lệnh bị lỗi.)

Vì vậy, một kỹ thuật để mô phỏng khối thử / bắt là khởi chạy một quy trình phụ để thực hiện công việc có -ebật. Sau đó, trong quy trình chính, kiểm tra mã trả về của quy trình phụ.

Bash hỗ trợ các chuỗi heredoc, vì vậy bạn không phải viết hai tệp riêng biệt để xử lý việc này. Trong ví dụ dưới đây, TRY heredoc sẽ chạy trong một trường hợp bash riêng, -eđược kích hoạt, vì vậy quy trình phụ sẽ sụp đổ nếu bất kỳ lệnh nào trả về mã thoát khác không. Sau đó, quay lại quy trình chính, chúng ta có thể kiểm tra mã trả về để xử lý khối bắt.

#!/bin/bash

set +e
bash -e <<TRY
  echo hello
  cd /does/not/exist
  echo world
TRY
if [ $? -ne 0 ]; then
  echo caught exception
fi

Đây không phải là một khối thử / bắt được hỗ trợ ngôn ngữ phù hợp, nhưng nó có thể làm giảm ngứa tương tự cho bạn.


6

Bạn có thể làm:

#!/bin/bash
if <command> ; then # TRY
    <do-whatever-you-want>
else # CATCH
    echo 'Exception'
    <do-whatever-you-want>
fi

4

Và bạn có bẫy http://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html không giống nhau, nhưng kỹ thuật khác bạn có thể sử dụng cho mục đích này


Tín hiệu thực sự chỉ liên quan bởi một luồng rất mỏng với khái niệm ngoại lệ và thử / bắt vì chúng không phải là một phần của luồng điều khiển thông thường của chương trình. Nhưng nó ổn khi đề cập đến ở đây.
Alfe

0

Một điều rất đơn giản tôi sử dụng:

try() {
    "$@" || (e=$?; echo "$@" > /dev/stderr; exit $e)
}

1
Vì phía bên phải ||là trong (), nó sẽ chạy trong một lớp con và thoát ra, mà không làm cho lớp vỏ chính thoát ra. Sử dụng { }nhóm thay thế.
codeforester

0

Dưới đây là một bản sao hoàn chỉnh của tập lệnh đơn giản hóa được sử dụng trong câu trả lời khác của tôi . Ngoài việc kiểm tra lỗi bổ sung, còn có một bí danh cho phép người dùng thay đổi tên của một bí danh hiện có. Cú pháp được đưa ra dưới đây. Nếu new_aliastham số bị bỏ qua, thì bí danh sẽ bị xóa.

ChangeAlias old_alias [new_alias]

Kịch bản hoàn chỉnh được đưa ra dưới đây.

common.GetAlias() {
    local "oldname=${1:-0}"
    if [[ $oldname =~ ^[0-9]+$ && oldname+1 -lt ${#FUNCNAME[@]} ]]; then
        oldname="${FUNCNAME[oldname + 1]}"
    fi
    name="common_${oldname#common.}"
    echo "${!name:-$oldname}"
}

common.Alias() {
    if [[ $# -ne 2 || -z $1 || -z $2 ]]; then
        echo "$(common.GetAlias): The must be only two parameters of nonzero length" >&2
        return 1;
    fi
    eval "alias $1='$2'"
    local "f=${2##*common.}"
    f="${f%%;*}"
    local "v=common_$f"
    f="common.$f"
    if [[ -n ${!v:-} ]]; then
        echo "$(common.GetAlias): $1: Function \`$f' already paired with name \`${!v}'" >&2
        return 1;
    fi
    shopt -s expand_aliases
    eval "$v=\"$1\""
}

common.ChangeAlias() {
    if [[ $# -lt 1 || $# -gt 2 ]]; then
        echo "usage: $(common.GetAlias) old_name [new_name]" >&2
        return "1"
    elif ! alias "$1" &>"/dev/null"; then
        echo "$(common.GetAlias): $1: Name not found" >&2
        return 1;
    fi
    local "s=$(alias "$1")" 
    s="${s#alias $1=\'}"
    s="${s%\'}"
    local "f=${s##*common.}"
    f="${f%%;*}"
    local "v=common_$f"
    f="common.$f"
    if [[ ${!v:-} != "$1" ]]; then
        echo "$(common.GetAlias): $1: Name not paired with a function \`$f'" >&2
        return 1;
    elif [[ $# -gt 1 ]]; then
        eval "alias $2='$s'"
        eval "$v=\"$2\""
    else
        unset "$v"
    fi
    unalias "$1"
}

common.Alias exception             'common.Exception'
common.Alias throw                 'common.Throw'
common.Alias try                   '{ if common.Try; then'
common.Alias yrt                   'common.EchoExitStatus; fi; common.yrT; }'
common.Alias catch                 '{ while common.Catch'
common.Alias hctac                 'common.hctaC -r; done; common.hctaC; }'
common.Alias finally               '{ if common.Finally; then'
common.Alias yllanif               'fi; common.yllaniF; }'
common.Alias caught                'common.Caught'
common.Alias EchoExitStatus        'common.EchoExitStatus'
common.Alias EnableThrowOnError    'common.EnableThrowOnError'
common.Alias DisableThrowOnError   'common.DisableThrowOnError'
common.Alias GetStatus             'common.GetStatus'
common.Alias SetStatus             'common.SetStatus'
common.Alias GetMessage            'common.GetMessage'
common.Alias MessageCount          'common.MessageCount'
common.Alias CopyMessages          'common.CopyMessages'
common.Alias TryCatchFinally       'common.TryCatchFinally'
common.Alias DefaultErrHandler     'common.DefaultErrHandler'
common.Alias DefaultUnhandled      'common.DefaultUnhandled'
common.Alias CallStack             'common.CallStack'
common.Alias ChangeAlias           'common.ChangeAlias'
common.Alias TryCatchFinallyAlias  'common.Alias'

common.CallStack() {
    local -i "i" "j" "k" "subshell=${2:-0}" "wi" "wl" "wn"
    local "format= %*s  %*s  %-*s  %s\n" "name"
    eval local "lineno=('' ${BASH_LINENO[@]})"
    for (( i=${1:-0},j=wi=wl=wn=0; i<${#FUNCNAME[@]}; ++i,++j )); do  
        name="$(common.GetAlias "$i")"
        let "wi = ${#j} > wi ? wi = ${#j} : wi"
        let "wl = ${#lineno[i]} > wl ? wl = ${#lineno[i]} : wl"
        let "wn = ${#name} > wn ? wn = ${#name} : wn"
    done
    for (( i=${1:-0},j=0; i<${#FUNCNAME[@]}; ++i,++j )); do
        ! let "k = ${#FUNCNAME[@]} - i - 1"
        name="$(common.GetAlias "$i")"
        printf "$format" "$wi" "$j" "$wl" "${lineno[i]}" "$wn" "$name" "${BASH_SOURCE[i]}"
    done
}

common.Echo() {
    [[ $common_options != *d* ]] || echo "$@" >"$common_file"
}

common.DefaultErrHandler() {
    echo "Orginal Status: $common_status"
    echo "Exception Type: ERR"
}

common.Exception() {
    common.TryCatchFinallyVerify || return
    if [[ $# -eq 0 ]]; then
        echo "$(common.GetAlias): At least one parameter is required" >&2
        return "1"         
    elif [[ ${#1} -gt 16 || -n ${1%%[0-9]*} || 10#$1 -lt 1 || 10#$1 -gt 255 ]]; then
        echo "$(common.GetAlias): $1: First parameter was not an integer between 1 and 255" >&2
        return "1"
    fi
    let "common_status = 10#$1"
    shift
    common_messages=()
    for message in "$@"; do
        common_messages+=("$message")
    done
    if [[ $common_options == *c* ]]; then
        echo "Call Stack:" >"$common_fifo"
        common.CallStack "2" >"$common_fifo"
    fi
}

common.Throw() {
    common.TryCatchFinallyVerify || return
    local "message"
    if ! common.TryCatchFinallyExists; then
        echo "$(common.GetAlias): No Try-Catch-Finally exists" >&2
        return "1"        
    elif [[ $# -eq 0 && common_status -eq 0 ]]; then
        echo "$(common.GetAlias): No previous unhandled exception" >&2 
        return "1"
    elif [[ $# -gt 0 && ( ${#1} -gt 16 || -n ${1%%[0-9]*} || 10#$1 -lt 1 || 10#$1 -gt 255 ) ]]; then
        echo "$(common.GetAlias): $1: First parameter was not an integer between 1 and 255" >&2
        return "1"
    fi
    common.Echo -n "In Throw ?=$common_status "
    common.Echo "try=$common_trySubshell subshell=$BASH_SUBSHELL #=$#"
    if [[ $common_options == *k* ]]; then
        common.CallStack "2" >"$common_file"
    fi
    if [[ $# -gt 0 ]]; then
        let "common_status = 10#$1"
        shift
        for message in "$@"; do
            echo "$message" >"$common_fifo"
        done
        if [[ $common_options == *c* ]]; then
            echo "Call Stack:" >"$common_fifo"
            common.CallStack "2" >"$common_fifo"
        fi
    elif [[ ${#common_messages[@]} -gt 0 ]]; then
        for message in "${common_messages[@]}"; do
            echo "$message" >"$common_fifo"
        done
    fi
    chmod "0400" "$common_fifo"
    common.Echo "Still in Throw $=$common_status subshell=$BASH_SUBSHELL #=$# -=$-"
    exit "$common_status"
}

common.ErrHandler() {
    common_status=$?
    trap ERR
    common.Echo -n "In ErrHandler ?=$common_status debug=$common_options "
    common.Echo "try=$common_trySubshell subshell=$BASH_SUBSHELL order=$common_order"
    if [[ -w "$common_fifo" ]]; then
        if [[ $common_options != *e* ]]; then
            common.Echo "ErrHandler is ignoring"
            common_status="0"
            return "$common_status" # value is ignored
        fi
        if [[ $common_options == *k* ]]; then
            common.CallStack "2" >"$common_file"
        fi
        common.Echo "Calling ${common_errHandler:-}"
        eval "${common_errHandler:-} \"${BASH_LINENO[0]}\" \"${BASH_SOURCE[1]}\" \"${FUNCNAME[1]}\" >$common_fifo <$common_fifo"
        if [[ $common_options == *c* ]]; then
            echo "Call Stack:" >"$common_fifo"
            common.CallStack "2" >"$common_fifo"
        fi
        chmod "0400" "$common_fifo"
    fi
    common.Echo "Still in ErrHandler $=$common_status subshell=$BASH_SUBSHELL -=$-"
    if [[ common_trySubshell -eq BASH_SUBSHELL ]]; then
        return "$common_status" # value is ignored   
    else
        exit "$common_status"
    fi
}

common.Token() {
    local "name"
    case $1 in
    b) name="before";;
    t) name="$common_Try";;
    y) name="$common_yrT";;
    c) name="$common_Catch";;
    h) name="$common_hctaC";;
    f) name="$common_yllaniF";;
    l) name="$common_Finally";;
    *) name="unknown";;
    esac
    echo "$name"
}

common.TryCatchFinallyNext() {
    common.ShellInit
    local "previous=$common_order" "errmsg"
    common_order="$2"
    if [[ $previous != $1 ]]; then
        errmsg="${BASH_SOURCE[2]}: line ${BASH_LINENO[1]}: syntax error_near unexpected token \`$(common.Token "$2")'"
        echo "$errmsg" >&2
        [[ /dev/fd/2 -ef $common_file ]] || echo "$errmsg" >"$common_file"
        kill -s INT 0
        return "1"        
    fi
}

common.ShellInit() {
    if [[ common_initSubshell -ne BASH_SUBSHELL ]]; then
        common_initSubshell="$BASH_SUBSHELL"
        common_order="b"
    fi
}

common.Try() {
    common.TryCatchFinallyVerify || return
    common.TryCatchFinallyNext "[byhl]" "t" || return 
    common_status="0"
    common_subshell="$common_trySubshell"
    common_trySubshell="$BASH_SUBSHELL"
    common_messages=()
    common.Echo "-------------> Setting try=$common_trySubshell at subshell=$BASH_SUBSHELL"
}

common.yrT() {
    local "status=$?"
    common.TryCatchFinallyVerify || return
    common.TryCatchFinallyNext "[t]" "y" || return 
    common.Echo -n "Entered yrT ?=$status status=$common_status "
    common.Echo "try=$common_trySubshell subshell=$BASH_SUBSHELL"
    if [[ common_status -ne 0 ]]; then    

        common.Echo "Build message array. ?=$common_status, subshell=$BASH_SUBSHELL"
        local "message=" "eof=TRY_CATCH_FINALLY_END_OF_MESSAGES_$RANDOM"
        chmod "0600" "$common_fifo"
        echo "$eof" >"$common_fifo"
        common_messages=()
        while read "message"; do

            common.Echo "----> $message"

            [[ $message != *$eof ]] || break
            common_messages+=("$message")
        done <"$common_fifo"
    fi

    common.Echo "In ytT status=$common_status"
    common_trySubshell="$common_subshell"
}

common.Catch() {
    common.TryCatchFinallyVerify || return
    common.TryCatchFinallyNext "[yh]" "c" || return 
    [[ common_status -ne 0 ]] || return "1"
    local "parameter" "pattern" "value"
    local "toggle=true" "compare=p" "options=$-"
    local -i "i=-1" "status=0"
    set -f
    for parameter in "$@"; do
        if "$toggle"; then
            toggle="false"
            if [[ $parameter =~ ^-[notepr]$ ]]; then
                compare="${parameter#-}"
                continue 
            fi
        fi
        toggle="true"
        while "true"; do
            eval local "patterns=($parameter)"
            if [[ ${#patterns[@]} -gt 0 ]]; then
                for pattern in "${patterns[@]}"; do
                    [[ i -lt ${#common_messages[@]} ]] || break
                    if [[ i -lt 0 ]]; then
                        value="$common_status"
                    else
                        value="${common_messages[i]}"
                    fi
                    case $compare in
                    [ne]) [[ ! $value == "$pattern" ]] || break 2;;
                    [op]) [[ ! $value == $pattern ]] || break 2;;
                    [tr]) [[ ! $value =~ $pattern ]] || break 2;;
                    esac
                done
            fi
            if [[ $compare == [not] ]]; then
                let "++i,1"
                continue 2
            else
                status="1"
                break 2
            fi
        done
        if [[ $compare == [not] ]]; then
            status="1"
            break
        else
            let "++i,1"
        fi
    done
    [[ $options == *f* ]] || set +f
    return "$status"
} 

common.hctaC() {
    common.TryCatchFinallyVerify || return
    common.TryCatchFinallyNext "[c]" "h" || return 
    [[ $# -ne 1 || $1 != -r ]] || common_status="0"
}

common.Finally() {
    common.TryCatchFinallyVerify || return
    common.TryCatchFinallyNext "[ych]" "f" || return 
}

common.yllaniF() {
    common.TryCatchFinallyVerify || return
    common.TryCatchFinallyNext "[f]" "l" || return 
    [[ common_status -eq 0 ]] || common.Throw
}

common.Caught() {
    common.TryCatchFinallyVerify || return
    [[ common_status -eq 0 ]] || return 1
}

common.EchoExitStatus() {
    return "${1:-$?}"
}

common.EnableThrowOnError() {
    common.TryCatchFinallyVerify || return
    [[ $common_options == *e* ]] || common_options+="e"
}

common.DisableThrowOnError() {
    common.TryCatchFinallyVerify || return
    common_options="${common_options/e}"
}

common.GetStatus() {
    common.TryCatchFinallyVerify || return
    echo "$common_status"
}

common.SetStatus() {
    common.TryCatchFinallyVerify || return
    if [[ $# -ne 1 ]]; then
        echo "$(common.GetAlias): $#: Wrong number of parameters" >&2
        return "1"         
    elif [[ ${#1} -gt 16 || -n ${1%%[0-9]*} || 10#$1 -lt 1 || 10#$1 -gt 255 ]]; then
        echo "$(common.GetAlias): $1: First parameter was not an integer between 1 and 255" >&2
        return "1"
    fi
    let "common_status = 10#$1"
}

common.GetMessage() {
    common.TryCatchFinallyVerify || return
    local "upper=${#common_messages[@]}"
    if [[ upper -eq 0 ]]; then
        echo "$(common.GetAlias): $1: There are no messages" >&2
        return "1"
    elif [[ $# -ne 1 ]]; then
        echo "$(common.GetAlias): $#: Wrong number of parameters" >&2
        return "1"         
    elif [[ ${#1} -gt 16 || -n ${1%%[0-9]*} || 10#$1 -ge upper ]]; then
        echo "$(common.GetAlias): $1: First parameter was an invalid index" >&2
        return "1"
    fi
    echo "${common_messages[$1]}"
}

common.MessageCount() {
    common.TryCatchFinallyVerify || return
    echo "${#common_messages[@]}"
}

common.CopyMessages() {
    common.TryCatchFinallyVerify || return
    if [[ $# -ne 1 ]]; then
        echo "$(common.GetAlias): $#: Wrong number of parameters" >&2
        return "1"         
    elif [[ ${#common_messages} -gt 0 ]]; then
        eval "$1=(\"\${common_messages[@]}\")"
    else
        eval "$1=()"
    fi
}

common.TryCatchFinallyExists() {
    [[ ${common_fifo:-u} != u ]]
}

common.TryCatchFinallyVerify() {
    local "name"
    if ! common.TryCatchFinallyExists; then
        echo "$(common.GetAlias "1"): No Try-Catch-Finally exists" >&2
        return "2"        
    fi
}

common.GetOptions() {
    local "opt"
    local "name=$(common.GetAlias "1")"
    if common.TryCatchFinallyExists; then
        echo "$name: A Try-Catch-Finally already exists" >&2
        return "1"        
    fi
    let "OPTIND = 1"
    let "OPTERR = 0"
    while getopts ":cdeh:ko:u:v:" opt "$@"; do
        case $opt in
        c)  [[ $common_options == *c* ]] || common_options+="c";;
        d)  [[ $common_options == *d* ]] || common_options+="d";;
        e)  [[ $common_options == *e* ]] || common_options+="e";;
        h)  common_errHandler="$OPTARG";;
        k)  [[ $common_options == *k* ]] || common_options+="k";;
        o)  common_file="$OPTARG";;
        u)  common_unhandled="$OPTARG";;
        v)  common_command="$OPTARG";;
        \?) #echo "Invalid option: -$OPTARG" >&2
            echo "$name: Illegal option: $OPTARG" >&2
            return "1";;
        :)  echo "$name: Option requires an argument: $OPTARG" >&2
            return "1";;
        *)  echo "$name: An error occurred while parsing options." >&2
            return "1";;
        esac
    done

    shift "$((OPTIND - 1))"
    if [[ $# -lt 1 ]]; then
        echo "$name: The fifo_file parameter is missing" >&2
        return "1"
    fi
    common_fifo="$1"
    if [[ ! -p $common_fifo ]]; then
        echo "$name: $1: The fifo_file is not an open FIFO" >&2
        return "1"  
    fi

    shift
    if [[ $# -lt 1 ]]; then
        echo "$name: The function parameter is missing" >&2
        return "1"
    fi
    common_function="$1"
    if ! chmod "0600" "$common_fifo"; then
        echo "$name: $common_fifo: Can not change file mode to 0600" >&2
        return "1"
    fi

    local "message=" "eof=TRY_CATCH_FINALLY_END_OF_FILE_$RANDOM"
    { echo "$eof" >"$common_fifo"; } 2>"/dev/null"
    if [[ $? -ne 0 ]]; then
        echo "$name: $common_fifo: Can not write" >&2
        return "1"
    fi   
    { while [[ $message != *$eof ]]; do
        read "message"
    done <"$common_fifo"; } 2>"/dev/null"
    if [[ $? -ne 0 ]]; then
        echo "$name: $common_fifo: Can not read" >&2
        return "1"
    fi   

    return "0"
}

common.DefaultUnhandled() {
    local -i "i"
    echo "-------------------------------------------------"
    echo "$(common.GetAlias "common.TryCatchFinally"): Unhandeled exception occurred"
    echo "Status: $(GetStatus)"
    echo "Messages:"
    for ((i=0; i<$(MessageCount); i++)); do
        echo "$(GetMessage "$i")"
    done
    echo "-------------------------------------------------"
}

common.TryCatchFinally() {
    local "common_file=/dev/fd/2"
    local "common_errHandler=common.DefaultErrHandler"
    local "common_unhandled=common.DefaultUnhandled"
    local "common_options="
    local "common_fifo="
    local "common_function="
    local "common_flags=$-"
    local "common_trySubshell=-1"
    local "common_initSubshell=-1"
    local "common_subshell"
    local "common_status=0"
    local "common_order=b"
    local "common_command="
    local "common_messages=()"
    local "common_handler=$(trap -p ERR)"
    [[ -n $common_handler ]] || common_handler="trap ERR"

    common.GetOptions "$@" || return "$?"
    shift "$((OPTIND + 1))"

    [[ -z $common_command ]] || common_command+="=$"
    common_command+='("$common_function" "$@")'

    set -E
    set +e
    trap "common.ErrHandler" ERR
    if true; then
        common.Try 
        eval "$common_command"
        common.EchoExitStatus
        common.yrT
    fi
    while common.Catch; do
        "$common_unhandled" >&2
        break
        common.hctaC -r
    done
    common.hctaC
    [[ $common_flags == *E* ]] || set +E
    [[ $common_flags != *e* ]] || set -e
    [[ $common_flags != *f* || $- == *f* ]] || set -f
    [[ $common_flags == *f* || $- != *f* ]] || set +f
    eval "$common_handler"
    return "$((common_status?2:0))"
}

0

Dưới đây là một ví dụ về một tập lệnh thực hiện try/catch/finallytrong bash.

Giống như các câu trả lời khác cho câu hỏi này, các ngoại lệ phải được bắt gặp sau khi thoát khỏi một quy trình con.

Các tập lệnh mẫu bắt đầu bằng cách tạo một fifo ẩn danh, được sử dụng để truyền các thông điệp chuỗi từ một command exceptionhoặc throwđến cuối trykhối gần nhất . Ở đây các tin nhắn được xóa khỏi fifo và được đặt trong một biến mảng. Trạng thái được trả về thông qua returnexitcác lệnh và được đặt trong một biến khác nhau. Để nhập một catchkhối, trạng thái này không được bằng không. Các yêu cầu khác để nhập một catchkhối được truyền dưới dạng tham số. Nếu kết thúc một catchkhối đạt được, thì trạng thái được đặt thành không. Nếu kết thúc finallykhối đạt được và trạng thái vẫn khác không, thì một cú ném ngầm chứa các thông điệp và trạng thái được thực thi. Kịch bản yêu cầu gọi hàm trycatchfinallychứa hàm xử lý ngoại lệ chưa được xử lý.

Cú pháp của trycatchfinallylệnh được đưa ra dưới đây.

trycatchfinally [-cde] [-h ERR_handler] [-k] [-o debug_file] [-u unhandled_handler] [-v variable] fifo function

Các -ctùy chọn thêm các cuộc gọi stack để các thông điệp ngoại lệ.
Các -dtùy chọn cho phép kết xuất debug.
Các -etùy chọn cho phép ngoại lệ lệnh.
Các -htùy chọn cho phép người dùng thay thế xử lý lệnh ngoại lệ riêng của họ.
Các -ktùy chọn thêm các cuộc gọi stack để kết xuất debug.
Các -otùy chọn thay thế các tập tin đầu ra mặc định là /dev/fd/2.
Các -utùy chọn cho phép người dùng thay thế xử lý ngoại lệ unhandled riêng của họ.
Các -vtùy chọn cho phép người dùng tùy chọn để vượt qua trở lại giá trị mặc dù việc sử dụng các lệnh Thay.
Tên fifotập tin fifo.
Hàm functionđược gọi trycatchfinallylà một quy trình con.

Lưu ý: Các cdkotùy chọn đã bị xóa để đơn giản hóa tập lệnh.

Cú pháp của catchlệnh được đưa ra dưới đây.

catch [[-enoprt] list ...] ...

Các tùy chọn được xác định dưới đây. Giá trị cho danh sách đầu tiên là trạng thái. Giá trị phụ là các thông điệp. Nếu có nhiều tin nhắn hơn danh sách, thì các tin nhắn còn lại sẽ bị bỏ qua.

-ecó nghĩa là [[ $value == "$string" ]](giá trị phải khớp với ít nhất một chuỗi trong danh sách)
-nnghĩa là [[ $value != "$string" ]](giá trị không thể khớp với bất kỳ chuỗi nào trong danh sách)
-ocó nghĩa là [[ $value != $pattern ]](giá trị không thể khớp với bất kỳ mẫu nào trong danh sách)
-pcó nghĩa là [[ $value == $pattern ]](giá trị có để khớp với ít nhất một mẫu trong danh sách)
-rcó nghĩa là [[ $value =~ $regex ]](giá trị phải khớp với ít nhất một biểu thức chính quy mở rộng trong danh sách)
-tcó nghĩa [[ ! $value =~ $regex ]](giá trị không thể khớp với bất kỳ biểu thức chính quy mở rộng nào trong danh sách)

Các try/catch/finallykịch bản được đưa ra dưới đây. Để đơn giản hóa tập lệnh cho câu trả lời này, hầu hết các kiểm tra lỗi đã được loại bỏ. Điều này làm giảm kích thước 64%. Một bản sao hoàn chỉnh của kịch bản này có thể được tìm thấy ở câu trả lời khác của tôi .

shopt -s expand_aliases
alias try='{ common.Try'
alias yrt='EchoExitStatus; common.yrT; }'
alias catch='{ while common.Catch'
alias hctac='common.hctaC; done; }'
alias finally='{ common.Finally'
alias yllanif='common.yllaniF; }'

DefaultErrHandler() {
    echo "Orginal Status: $common_status"
    echo "Exception Type: ERR"
}

exception() {
    let "common_status = 10#$1"
    shift
    common_messages=()
    for message in "$@"; do
        common_messages+=("$message")
    done
}

throw() {
    local "message"
    if [[ $# -gt 0 ]]; then
        let "common_status = 10#$1"
        shift
        for message in "$@"; do
            echo "$message" >"$common_fifo"
        done
    elif [[ ${#common_messages[@]} -gt 0 ]]; then
        for message in "${common_messages[@]}"; do
            echo "$message" >"$common_fifo"
        done
    fi
    chmod "0400" "$common_fifo"
    exit "$common_status"
}

common.ErrHandler() {
    common_status=$?
    trap ERR
    if [[ -w "$common_fifo" ]]; then
        if [[ $common_options != *e* ]]; then
            common_status="0"
            return
        fi
        eval "${common_errHandler:-} \"${BASH_LINENO[0]}\" \"${BASH_SOURCE[1]}\" \"${FUNCNAME[1]}\" >$common_fifo <$common_fifo"
        chmod "0400" "$common_fifo"
    fi
    if [[ common_trySubshell -eq BASH_SUBSHELL ]]; then
        return   
    else
        exit "$common_status"
    fi
}

common.Try() {
    common_status="0"
    common_subshell="$common_trySubshell"
    common_trySubshell="$BASH_SUBSHELL"
    common_messages=()
}

common.yrT() {
    local "status=$?"
    if [[ common_status -ne 0 ]]; then    
        local "message=" "eof=TRY_CATCH_FINALLY_END_OF_MESSAGES_$RANDOM"
        chmod "0600" "$common_fifo"
        echo "$eof" >"$common_fifo"
        common_messages=()
        while read "message"; do
            [[ $message != *$eof ]] || break
            common_messages+=("$message")
        done <"$common_fifo"
    fi
    common_trySubshell="$common_subshell"
}

common.Catch() {
    [[ common_status -ne 0 ]] || return "1"
    local "parameter" "pattern" "value"
    local "toggle=true" "compare=p" "options=$-"
    local -i "i=-1" "status=0"
    set -f
    for parameter in "$@"; do
        if "$toggle"; then
            toggle="false"
            if [[ $parameter =~ ^-[notepr]$ ]]; then
                compare="${parameter#-}"
                continue 
            fi
        fi
        toggle="true"
        while "true"; do
            eval local "patterns=($parameter)"
            if [[ ${#patterns[@]} -gt 0 ]]; then
                for pattern in "${patterns[@]}"; do
                    [[ i -lt ${#common_messages[@]} ]] || break
                    if [[ i -lt 0 ]]; then
                        value="$common_status"
                    else
                        value="${common_messages[i]}"
                    fi
                    case $compare in
                    [ne]) [[ ! $value == "$pattern" ]] || break 2;;
                    [op]) [[ ! $value == $pattern ]] || break 2;;
                    [tr]) [[ ! $value =~ $pattern ]] || break 2;;
                    esac
                done
            fi
            if [[ $compare == [not] ]]; then
                let "++i,1"
                continue 2
            else
                status="1"
                break 2
            fi
        done
        if [[ $compare == [not] ]]; then
            status="1"
            break
        else
            let "++i,1"
        fi
    done
    [[ $options == *f* ]] || set +f
    return "$status"
} 

common.hctaC() {
    common_status="0"
}

common.Finally() {
    :
}

common.yllaniF() {
    [[ common_status -eq 0 ]] || throw
}

caught() {
    [[ common_status -eq 0 ]] || return 1
}

EchoExitStatus() {
    return "${1:-$?}"
}

EnableThrowOnError() {
    [[ $common_options == *e* ]] || common_options+="e"
}

DisableThrowOnError() {
    common_options="${common_options/e}"
}

GetStatus() {
    echo "$common_status"
}

SetStatus() {
    let "common_status = 10#$1"
}

GetMessage() {
    echo "${common_messages[$1]}"
}

MessageCount() {
    echo "${#common_messages[@]}"
}

CopyMessages() {
    if [[ ${#common_messages} -gt 0 ]]; then
        eval "$1=(\"\${common_messages[@]}\")"
    else
        eval "$1=()"
    fi
}

common.GetOptions() {
    local "opt"
    let "OPTIND = 1"
    let "OPTERR = 0"
    while getopts ":cdeh:ko:u:v:" opt "$@"; do
        case $opt in
        e)  [[ $common_options == *e* ]] || common_options+="e";;
        h)  common_errHandler="$OPTARG";;
        u)  common_unhandled="$OPTARG";;
        v)  common_command="$OPTARG";;
        esac
    done
    shift "$((OPTIND - 1))"
    common_fifo="$1"
    shift
    common_function="$1"
    chmod "0600" "$common_fifo"
}

DefaultUnhandled() {
    local -i "i"
    echo "-------------------------------------------------"
    echo "TryCatchFinally: Unhandeled exception occurred"
    echo "Status: $(GetStatus)"
    echo "Messages:"
    for ((i=0; i<$(MessageCount); i++)); do
        echo "$(GetMessage "$i")"
    done
    echo "-------------------------------------------------"
}

TryCatchFinally() {
    local "common_errHandler=DefaultErrHandler"
    local "common_unhandled=DefaultUnhandled"
    local "common_options="
    local "common_fifo="
    local "common_function="
    local "common_flags=$-"
    local "common_trySubshell=-1"
    local "common_subshell"
    local "common_status=0"
    local "common_command="
    local "common_messages=()"
    local "common_handler=$(trap -p ERR)"
    [[ -n $common_handler ]] || common_handler="trap ERR"
    common.GetOptions "$@"
    shift "$((OPTIND + 1))"
    [[ -z $common_command ]] || common_command+="=$"
    common_command+='("$common_function" "$@")'
    set -E
    set +e
    trap "common.ErrHandler" ERR
    try
        eval "$common_command"
    yrt
    catch; do
        "$common_unhandled" >&2
    hctac
    [[ $common_flags == *E* ]] || set +E
    [[ $common_flags != *e* ]] || set -e
    [[ $common_flags != *f* || $- == *f* ]] || set -f
    [[ $common_flags == *f* || $- != *f* ]] || set +f
    eval "$common_handler"
}

Dưới đây là một ví dụ, giả sử tập lệnh trên được lưu trữ trong tệp có tên simple. Các makefifotập tin có chứa các kịch bản được mô tả trong câu trả lời này . Giả định được đưa ra là tệp có tên 4444kkkkkkhông tồn tại, do đó gây ra ngoại lệ xảy ra. Đầu ra thông báo lỗi từ ls 4444kkkkklệnh được tự động loại bỏ cho đến khi bên trong catchkhối thích hợp .

#!/bin/bash
#

if [[ $0 != ${BASH_SOURCE[0]} ]]; then
    bash "${BASH_SOURCE[0]}" "$@"
    return
fi

source simple
source makefifo

MyFunction3() {
    echo "entered MyFunction3" >&4
    echo "This is from MyFunction3"
    ls 4444kkkkk
    echo "leaving MyFunction3" >&4
}

MyFunction2() {
    echo "entered MyFunction2" >&4
    value="$(MyFunction3)"
    echo "leaving MyFunction2" >&4
}

MyFunction1() {
    echo "entered MyFunction1" >&4
    local "flag=false"
    try 
    (
        echo "start of try" >&4
        MyFunction2
        echo "end of try" >&4
    )
    yrt
    catch "[1-3]" "*" "Exception\ Type:\ ERR"; do
        echo 'start of catch "[1-3]" "*" "Exception\ Type:\ ERR"'
        local -i "i"
        echo "-------------------------------------------------"
        echo "Status: $(GetStatus)"
        echo "Messages:"
        for ((i=0; i<$(MessageCount); i++)); do
            echo "$(GetMessage "$i")"
        done
        echo "-------------------------------------------------"
        break
        echo 'end of catch "[1-3]" "*" "Exception\ Type:\ ERR"'
    hctac >&4
    catch "1 3 5" "*" -n "Exception\ Type:\ ERR"; do
        echo 'start of catch "1 3 5" "*" -n "Exception\ Type:\ ERR"'
        echo "-------------------------------------------------"
        echo "Status: $(GetStatus)"
        [[ $(MessageCount) -le 1 ]] || echo "$(GetMessage "1")"
        echo "-------------------------------------------------"
        break
        echo 'end of catch "1 3 5" "*" -n "Exception\ Type:\ ERR"'
    hctac >&4
    catch; do
        echo 'start of catch' >&4
        echo "failure"
        flag="true"
        echo 'end of catch' >&4
    hctac
    finally
        echo "in finally"
    yllanif >&4
    "$flag" || echo "success"
    echo "leaving MyFunction1" >&4
} 2>&6

ErrHandler() {
    echo "EOF"
    DefaultErrHandler "$@"
    echo "Function: $3"
    while read; do
        [[ $REPLY != *EOF ]] || break
        echo "$REPLY"
    done
}

set -u
echo "starting" >&2
MakeFIFO "6"
TryCatchFinally -e -h ErrHandler -o /dev/fd/4 -v result /dev/fd/6 MyFunction1 4>&2
echo "result=$result"
exec >&6-

Kịch bản trên đã được thử nghiệm bằng cách sử dụng GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17). Đầu ra, từ việc chạy tập lệnh này, được hiển thị bên dưới.

starting
entered MyFunction1
start of try
entered MyFunction2
entered MyFunction3
start of catch "[1-3]" "*" "Exception\ Type:\ ERR"
-------------------------------------------------
Status: 1
Messages:
Orginal Status: 1
Exception Type: ERR
Function: MyFunction3
ls: 4444kkkkk: No such file or directory
-------------------------------------------------
start of catch
end of catch
in finally
leaving MyFunction1
result=failure

Một ví dụ khác sử dụng a throwcó thể được tạo bằng cách thay thế hàm MyFunction3bằng tập lệnh hiển thị bên dưới.

MyFunction3() {
    echo "entered MyFunction3" >&4
    echo "This is from MyFunction3"
    throw "3" "Orginal Status: 3" "Exception Type: throw"
    echo "leaving MyFunction3" >&4
}

Cú pháp của throwlệnh được đưa ra dưới đây. Nếu không có tham số nào, thì trạng thái và thông điệp được lưu trữ trong các biến được sử dụng thay thế.

throw [status] [message ...]

Đầu ra, từ việc thực thi tập lệnh đã sửa đổi, được hiển thị bên dưới.

starting
entered MyFunction1
start of try
entered MyFunction2
entered MyFunction3
start of catch "1 3 5" "*" -n "Exception\ Type:\ ERR"
-------------------------------------------------
Status: 3
Exception Type: throw
-------------------------------------------------
start of catch
end of catch
in finally
leaving MyFunction1
result=failure
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.