首页 文章

Bash中是否有TRY CATCH命令

提问于
浏览
203

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

10 回答

  • 9

    Bash中有TRY CATCH命令吗?

    没有 .

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

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

    使用 ||

    如果 command1 失败,则 command2 运行如下

    command1 || command2
    

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

    try/catch 的最接近的近似如下

    { # try
    
        command1 &&
        #save your output
    
    } || { # catch
        # save log for exception 
    }
    

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

    set -e
    

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

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

  • 4

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

    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
    }
    

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

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

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

    try 
        echo 'Hello'
        false
        echo 'This will not be displayed'
    
    catch 
        echo "Error in $__EXCEPTION_SOURCE__ at line: $__EXCEPTION_LINE__!"
    

    你甚至可以将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__!"
    }
    

    代码是我bash boilerplate/framework的一部分 . 它进一步扩展了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
    }
    

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

  • 0

    bash 在中断检测到错误状态时不会中止正在运行的执行(除非您设置了 -e 标志) . 提供 try/catch 的编程语言是为了禁止"bailing out",因为这种特殊情况(因此通常称为"exception") .

    而在 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
    

    你可以尝试一个命令而不是 some_condition 而不是 some_condition ,如果它失败(退出代码大于0),纾困:

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

    不幸的是,使用这种技术,你只能使用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"
    )
    ...
    
  • 7

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

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

    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
    

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

  • 74

    你可以使用 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
    )
    
  • 11

    有这么多类似的解决方案可能有效 . 下面是一个简单而有效的方法来完成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
    
  • 53

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

  • 1

    你可以做:

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

    我使用的一件非常简单的事情:

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

相关问题