Bash 中是否有 TRY CATCH 命令

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22009364/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 00:41:25  来源:igfitidea点击:

Is there a TRY CATCH command in Bash

bashshellerror-handling

提问by Lee Probert

I'm writing a shell script and need to check that a terminal app has been installed. I want to use a TRY/CATCH command to do this unless there is a neater way.

我正在编写一个 shell 脚本,需要检查是否已安装终端应用程序。除非有更简洁的方法,否则我想使用 TRY/CATCH 命令来执行此操作。

回答by Jayesh Bhoi

Is there a TRY CATCH command in Bash?

Bash 中是否有 TRY CATCH 命令?

No.

不。

Bash doesn't have as many luxuries as one can find in many programming languages.

Bash 没有像许多编程语言中那样多的优点。

There is no try/catchin bash; however, one can achieve similar behavior using &&or ||.

try/catchbash 中没有;但是,可以使用&&或实现类似的行为||

Using ||:

使用||

if command1fails then command2runs as follows

如果command1失败则command2运行如下

command1 || command2

Similarly, using &&, command2will run if command1is successful

同样,使用&&,command2将在command1成功时运行

The closest approximation of try/catchis as follows

最接近的近似try/catch如下

{ # try

    command1 &&
    #save your output

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

Also bash contains some error handling mechanisms, as well

bash 还包含一些错误处理机制,以及

set -e

it stops your script if any simple command fails.

如果任何简单命令失败,它会停止您的脚本。

And also why not if...else. It is your best friend.

还有为什么不呢if...else。它是你最好的朋友。

回答by Mathias Henze

Based on some answers I found here, I made myself a small helper file to source for my projects:

根据我在这里找到的一些答案,我为自己制作了一个小帮助文件来为我的项目提供资源:

trycatch.sh

尝试捕获.sh

#!/bin/bash

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

function throw()
{
    exit 
}

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

function throwErrors()
{
    set -e
}

function ignoreErrors()
{
    set +e
}

here is an example how it looks like in use:

这是一个示例,它在使用中的样子:

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

回答by niieani

I've developed an almost flawless try & catch implementation in bash, that allows you to write code like:

我在 bash 中开发了一个几乎完美的 try & catch 实现,它允许您编写如下代码:

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

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

You can even nest the try-catch blocks inside themselves!

您甚至可以将 try-catch 块嵌套在其内部!

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__!"
}

The code is a part of my bash boilerplate/framework. It further extends the idea of try & catch with things like error handling with backtrace and exceptions (plus some other nice features).

代码是我的bash 样板/框架的一部分。它进一步扩展了 try & catch 的想法,例如使用回溯和异常处理错误(以及其他一些不错的功能)。

Here's the code that's responsible just for try & catch:

这是仅负责 try & catch 的代码:

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 "" > /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=
    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
}

Feel free to use, fork and contribute - it's on GitHub.

随意使用、分叉和贡献——它在GitHub 上

回答by Mark K Cowan

You can use trap:

您可以使用trap

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

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

translates to:

翻译成:

(
  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
)

回答by Kostanos

There are so many similar solutions which probably work. Below is a simple and working way to accomplish try/catch, with explanation in the comments.

有很多类似的解决方案可能有效。下面是一种完成 try/catch 的简单而有效的方法,并在注释中进行了解释。

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

回答by Alfe

bashdoes not abort the running execution in case something detects an error state (unless you set the -eflag). Programming languages which offer try/catchdo this in order to inhibita "bailing out" because of this special situation (hence typically called "exception").

bash如果检测到错误状态,则不会中止正在运行的执行(除非您设置了-e标志)。由于这种特殊情况(因此通常称为“异常”),提供try/catch此功能的编程语言是为了抑制“救助”。

In the bash, instead, only the command in question will exit with an exit code greater than 0, indicating that error state. You can check for that of course, but since there is no automatic bailing outof anything, a try/catchdoes not make sense. It is just lacking that context.

bash, 相反,只有有问题的命令会以大于 0 的退出代码退出,表明错误状态。你当然可以检查一下,但由于没有自动退出任何东西,try/catch没有意义。它只是缺乏这种背景。

You can, however, simulate a bailing outby using sub shells which can terminate at a point you decide:

但是,您可以使用可以在您决定的点终止的子外壳来模拟救助

(
  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

Instead of that some_conditionwith an ifyou also can just try a command, and in case it fails(has an exit code greater than 0), bail out:

除了some_condition使用 anif你还可以尝试一个命令,如果它失败(退出代码大于 0),请退出:

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

Unfortunately, using this technique you are restricted to 255 different exit codes (1..255) and no decent exception objects can be used.

不幸的是,使用这种技术,您只能使用 255 个不同的退出代码 (1..255),并且无法使用合适的异常对象。

If you need more information to pass along with your simulated exception, you can use the stdout of the subshells, but that is a bit complicated and maybe another question ;-)

如果您需要更多信息与模拟异常一起传递,您可以使用子shell的标准输出,但这有点复杂,也许是另一个问题;-)

Using the above mentioned -eflag to the shell you can even strip that explicit exitstatement:

-e在 shell 中使用上面提到的标志,您甚至可以删除该显式exit语句:

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

回答by Dan Fabulich

As everybody says, bash doesn't have a proper language-supported try/catch syntax. You can launch bash with the -eargument or use set -einside the script to abort the entire bash process if any command has a non-zero exit code. (You can also set +eto temporarily allow failing commands.)

正如大家所说,bash 没有适当的语言支持的 try/catch 语法。如果任何命令具有非零退出代码,您可以-e使用参数启动 bash或set -e在脚本中使用以中止整个 bash 进程。(您也set +e可以暂时允许失败的命令。)

So, one technique to simulate a try/catch block is to launch a sub-process to do the work with -eenabled. Then in the main process, check the return code of the sub-process.

因此,模拟 try/catch 块的一种技术是启动一个子进程来执行-e启用的工作。然后在主进程中,检查子进程的返回码。

Bash supports heredoc strings, so you don't have to write two separate files to handle this. In the below example, the TRY heredoc will run in a separate bash instance, with -eenabled, so the sub-process will crash if any command returns a non-zero exit code. Then, back in the main process, we can check the return code to handle a catch block.

Bash 支持 heredoc 字符串,因此您不必编写两个单独的文件来处理这个问题。在下面的示例中,TRY heredoc 将在单独的 bash 实例中运行,并-e启用,因此如果任何命令返回非零退出代码,子进程将崩溃。然后,回到主进程,我们可以检查返回代码以处理 catch 块。

#!/bin/bash

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

It's not a proper language-supported try/catch block, but it may scratch a similar itch for you.

它不是一个适当的语言支持的 try/catch 块,但它可能会为您带来类似的痒。

回答by Davidson Lima

You can do:

你可以做:

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

回答by Eran Ben-Natan

And you have traps http://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.htmlwhich is not the same, but other technique you can use for this purpose

你有陷阱http://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html这是不一样的,但是你可以使用其他技术来达到这个目的

回答by syberghost

A very simple thing I use:

我使用的一个非常简单的东西:

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