Bash中是否有TRY CATCH命令

Lee*_*ert 280 error-handling bash shell

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

Jay*_*hoi 471

Bash中有TRY CATCH命令吗?

没有.

Bash并没有像许多编程语言中那样多的奢侈品.

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

使用||:

如果command1失败则command2运行如下

command1 || command2
Run Code Online (Sandbox Code Playgroud)

同样,使用&&,command2如果command1成功将运行

最接近的近似try/catch如下

{ # try

    command1 &&
    #save your output

} || { # catch
    # save log for exception 
}
Run Code Online (Sandbox Code Playgroud)

bash也包含一些错误处理机制

set -e
Run Code Online (Sandbox Code Playgroud)

如果简单命令失败,它将立即停止您的脚本.我认为这应该是默认行为.由于这些错误几乎总是意味着意外的事情,因此继续执行以下命令并不是"理智".

而且为什么不呢if...else.这是你最好的朋友.

  • 有了这个,你需要注意`#save你的输出`的代码不会失败,或者"catch"块仍然会执行. (16认同)
  • 有人建议使用`if ... else`结构.这是否意味着bash命令如果成功运行则会解析为"truthy",如果失败则会解析为"falsy"? (5认同)
  • 对于这个帖子的读者:似乎`set -e`不一定是最好的做事方式; 这里有一些反驳/特殊情况:http://mywiki.wooledge.org/BashFAQ/105 (5认同)
  • 我的代码在删除“&&”后可以工作。`&&` 用于连接 `try` 块中的两个命令。它不应该添加在“try”块的末尾。 (5认同)
  • 我可以知道如何记录异常吗?通常在java代码中,我们可以使用system.out.log(e),但是在shell中呢? (2认同)
  • 我将脚本粘贴到您的答案上,但我保留了您编写的换行符,当我这样做时,我收到“意外令牌附近的语法错误}”错误 (2认同)
  • 答案中的@alper脚本是一个示例,您必须根据您使用的命令类型进行修改。前任。`#!/bin/bash { echo "这是尝试" && } || { echo "这是捕获" }` (2认同)

Mat*_*nze 93

基于我在这里找到的一些答案,我为自己的项目提供了一个小帮手文件:

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
}
Run Code Online (Sandbox Code Playgroud)

这是一个如何使用的示例:

#!/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
}
Run Code Online (Sandbox Code Playgroud)

  • 你能告诉你如何将 try catch 函数导入到另一个例子中吗?(我假设它们在单独的文件中) (3认同)
  • @MathiasHenze 谢谢,你的代码太酷了。但是为什么在 `catch` 之后和 `{}` 块之前需要一个 `||`?我会认为它是一个`&&` (2认同)

nii*_*ani 59

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

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

catch 
    echo "Error in $__EXCEPTION_SOURCE__ at line: $__EXCEPTION_LINE__!"
Run Code Online (Sandbox Code Playgroud)

你甚至可以将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__!"
}
Run Code Online (Sandbox Code Playgroud)

代码是我的bash样板/框架的一部分.它进一步扩展了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 "$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
}
Run Code Online (Sandbox Code Playgroud)

随意使用,分叉和贡献 - 它在GitHub上.

  • @erm3nda 很高兴听到这个消息!我认为在发布此内容后我已经消除了一些错误,因此请查看 GitHub 以获取更新(您需要包含 03_exception.sh 和 04_try_catch.sh)。据我所知,当前版本几乎是防弹的。 (2认同)
  • 这里有一个基本问题:如果您更改 try 块中的变量,外部将看不到它,因为它在子 shell 中运行。 (2认同)

Mar*_*wan 14

你可以使用trap:

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

翻译为:

(
  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
)
Run Code Online (Sandbox Code Playgroud)


Alf*_*lfe 12

bash如果sth检测到错误状态,则不会中止正在运行的执行(除非您设置了-e标志).提供try/catch这样的编程语言,以便由于这种特殊情况(因此通常称为"异常")而禁止 "拯救".

bash相反,仅在有关的命令将退出代码大于0退出,指示错误状态.当然可以检查,但由于没有自动拯救任何东西,尝试/捕获没有意义.它只是缺乏这种背景.

但是,你可以通过使用子shell来模拟拯救,这些子shell可以在你决定的点终止:

(
  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
Run Code Online (Sandbox Code Playgroud)

取而代之的是some_conditionif你也可以仅仅尝试了命令,如果它失败(有一个退出代码大于0),摆脱困境:

(
  echo "Do one thing"
  echo "Do another thing"
  some_command || exit 3
  echo "Do yet another thing"
  echo "And do a last thing"
)
...
Run Code Online (Sandbox Code Playgroud)

不幸的是,使用这种技术,你只能使用255个不同的退出代码(1..255),并且不能使用任何体面的异常对象.

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

使用上面提到-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"
)
...
Run Code Online (Sandbox Code Playgroud)

  • 这确实应该是公认的答案,因为它最接近您可以使用 shell 获得的 try/catch 逻辑。 (3认同)

Dan*_*ich 12

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

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

Bash支持heredoc字符串,因此您不必编写两个单独的文件来处理它.在下面的示例中,TRY heredoc将在-e启用的单独bash实例中运行,因此如果任何命令返回非零退出代码,则子进程将崩溃.然后,回到主进程,我们可以检查返回代码来处理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
Run Code Online (Sandbox Code Playgroud)

它不是一个适当的语言支持的try/catch块,但它可能会为你划伤类似的痒.


Kos*_*nos 12

有这么多类似的解决方案可能有效.下面是一个简单而有效的方法来完成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
Run Code Online (Sandbox Code Playgroud)


Sea*_*ayo 9

我用这样的东西:

YOUR COMMAND HERE
EXITCODE=$?
if [ "$EXITCODE" -ne "0" ]; then
    #this is the catch part
    echo "uh oh"
    exit $EXITCODE
fi
Run Code Online (Sandbox Code Playgroud)

这只是检查命令的退出代码并将其与零进行比较(这表明命令运行成功)


Dav*_*ima 7

你可以做:

#!/bin/bash
if <command> ; then # TRY
    <do-whatever-you-want>
else # CATCH
    echo 'Exception'
    <do-whatever-you-want>
fi
Run Code Online (Sandbox Code Playgroud)


Era*_*tan 6

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