zoukankan      html  css  js  c++  java
  • PowerShell 异常处理

    在使用 PowerShell 的过程中,发现它的异常处理并不像想象中的那么直观,所以在这里总结一下。

    Terminating Errors

    通过 ThrowTerminatingError 触发的错误称为 Terminating Errors。本质上它是创建了一个异常,所以我们可以使用 catch 语句来捕获 Terminating Errors。因此 Terminating Errors 的另外一个名字叫 Exceptions。默认情况下,Terminating Errors 不影响后面命令的执行!

    把下面的代码保存到文件 exception.ps1 中:

    # 下面的命令不存在
    Get-TerminatingError
    
    Write-Host 'hello world'

    然后执行脚本 exception.ps1:

    注意最后输出的 "hello world",虽然执行过程中出现了错误,但是错误后面的代码依然被执行了。
    Terminating Errors 的特点是默认情况下你可以通过 catch 语句捕获它,从而控制代码的执行流。
    把下面的代码保存到文件 exception.ps1 中:

    Try{
        # 下面的命令不存在
        Get-TerminatingError
    }
    Catch{
        Write-Host 'got you'
        exit 1
    }
    Write-Host 'hello world'

    然后执行脚本 exception.ps1:

    这样就好多了,我们预测了代码中可能产生的问题,并且在发生了错误的情况下果断的结束了脚本的执行。

    虽然 Terminating Errors 不能中断脚本的执行,但是却可以中断 pipeline 的执行。从 Terminating Errors 的文档中我们可以看到,其内部抛出了 PipelineStoppedException 异常,并且 PowerShell 内部处理了这个异常。所以正在执行的 pipeline 会被中断掉,然后继续执行后面的其它代码。

    Non-Terminating Errors

    通过 Write-Error 触发的错误称为 Non-Terminating Errors。本质上它只是把错误信息写入了输出流中而没有产生异常,所以默认情况下 catch 语句无法捕获 Non-Terminating Errors。默认情况下,Non-Terminating Errors 不影响后面命令的执行!

    把下面的代码保存到文件 exception.ps1 中:

    # C:xxx 不存在
    Copy-Item C:xxx
    Write-Host 'hello world'

    然后执行脚本 exception.ps1:

    注意最后输出的 "hello world",虽然执行过程中出现了错误,但是错误后面的代码依然被执行了。

    Non-Terminating Errors 的特点是默认情况下 catch 语句无法捕获它!
    把下面的代码保存到文件 exception.ps1 中:

    Try{
        # C:xxx 不存在
        Copy-Item C:xxx
    }
    Catch{
        Write-Host 'got you'
        exit 1
    }
    Write-Host 'hello world'

    然后执行脚本 exception.ps1,结果和上面是一样的,错误后面的代码依然会被执行。

    ErrorAction 选项的秘密

    Non-Terminating Errors 默认只是通过 Write-Error 把错误信息写入了输出流中而没有产生异常,所以 catch 语句无法捕获 Non-Terminating Errors。但是我们却可以通过 -ErrorAction 选项改变 WriteError 的默认行为。
    ErrorAction 选项主要用来改变命令的 non-terminating errors 的行为(它不会对 Terminating Errors 产生影响)!
    ErrorAction 选项的工作原理为:用指定的参数覆盖当前命令的 $ErrorActionPreference 变量。默认情况下  $ErrorActionPreference 变量的值为 Continue。

    可以为 -ErrorAction 选项指定下面的参数:

    -ErrorAction[:{Continue | Ignore | Inquire | SilentlyContinue | Stop | Suspend }]

    它们表示的含义如下:
    Continue   显示错误信息并继续执行后面的命令,这是默认值。
    Ignore       这个值是在 PowerShell 3.0 引入的。它不显示错误信息并继续执行后面的命令。与 SilentlyContinue 不同的是,它也不会把错误信息添加到 $Error 变量中。
    Inquire      显示错误信息并弹框与用户交互。
    SilentlyContinue   不显示错误信息并继续执行后面的命令。
    Stop          显示错误信息并且退出脚本的执行。
    Suspend    这个值只适用于 workflow。当 terminating error 发生时执行会暂停下来,然后决定是否恢复执行。

    这里我们重点关注使用比较多的 stop, 它会让 Write-Error 等原本产生 non-terminating error 的命令产生 terminating error!所以我们就可以用 catch 语句来捕获异常了。把下面的代码保存到文件 exception.ps1 中:

    Try{
        # C:xxx 不存在
        Copy-Item C:xxx -ErrorAction Stop
    }
    Catch{
        Write-Host 'got you'
        exit 1
    }
    Write-Host 'hello world'

    注意我们为 Copy-Item 命令添加了 -ErrorAction Stop 选项,然后执行脚本 exception.ps1:

    哈哈,这次捕获到异常了,并且最后也没有输出 "hello world"。

    如果需要给每个命令都添加 -ErrorAction 选项可不是什么好玩的事情,好在我们可以在脚本中设置 $ErrorActionPreference 变量来完成同样的功能:

    $ErrorActionPreference = 'Stop'

    这样在当前的脚本中,所有原本的 non-terminating error 都会变成 terminating error。

    Try/Catch/Finally

    在异常处理中不介绍 Try/Catch/Finally 语句是不完整的。Catch 语句用来捕获 Try 块中产生的异常,当然我们可以指定只捕获那些我们感兴趣的异常。
    Finally 块也是非常重要的,它能保证一些必要的逻辑被执行,比如释放数据库连接。下面的 demo 演示了如何在脚本中设置 $ErrorActionPreference 变量:

    $eap = $ErrorActionPreference
    Try{
        $ErrorActionPreference = 'Stop'
        # do something
    }
    Catch{
        Write-Host "error !"
        Exit 1
    }
    Finally{
        $ErrorActionPreference = $eap
    }

    在 Finally 块中把 $ErrorActionPreference 变量还原,保证我们自己设置的 $ErrorActionPreference 变量只影响 Try/Catch 块中的语句。

    $PSItem 变量

    $PSItem 是一个 ErrorRecord 类型的变量,它会保存异常的详细信息。在捕获到异常时,我们可以把异常相关的信息输出或保存到日志中:

    $eap = $ErrorActionPreference
    Try{
        $ErrorActionPreference = 'Stop'
        # 下面的命令不存在
        Get-TerminatingError
    }
    Catch{
        # 比较简洁的信息
        Write-Output $PSItem.ToString()    
    }
    Finally{
        $ErrorActionPreference = $eap
    }

    $PSItem.ToString() 中只有错误的描述,看起来会比较简洁。执行上面的脚本:

    红框中的信息就是 $PSItem.ToString() 提供的。
    仅有错误信息并不总是能够很好的帮助调查问题的根源,如果有出错的行号和异常堆栈会好很多:

    $eap = $ErrorActionPreference
    Try{
        $ErrorActionPreference = 'Stop'
        # 下面的命令不存在
        Get-TerminatingError
    }
    Catch{    
        # 包含堆栈的信息
        Write-Output $PSItem
    }
    Finally{
        $ErrorActionPreference = $eap
    }

    这次我们直接输出了 $PSItem,执行上面的代码会得到下面的输出:

    这下好多了,有了出错的行号和调用堆栈就可以很容易的看到出错的原因。

    结论

    作为一门脚本语言,PowerShell 对异常的支持还是非常强大的。不过像 Terminating Errors 和 Non-Terminating Errors 这样的特性也会给我们带来不少的困扰。希望本文介绍的内容可以帮助大家更好的理解 PowerShell 中异常相关的概念。

  • 相关阅读:
    吴裕雄--天生自然C++语言学习笔记:C++ 引用
    吴裕雄--天生自然C++语言学习笔记:C++ 指针
    吴裕雄--天生自然C++语言学习笔记:C++ 字符串
    吴裕雄--天生自然C++语言学习笔记:C++ 数组
    吴裕雄--天生自然C++语言学习笔记:C++ 数字
    吴裕雄--天生自然C++语言学习笔记:C++ 函数
    吴裕雄--天生自然C++语言学习笔记:C++ 判断
    HiHoCoder1671 : 反转子串([Offer收割]编程练习赛41)(占位)
    HihoCoder1670 : 比赛日程安排([Offer收割]编程练习赛41)(模拟)
    POJ3417Network(LCA+树上查分||树剖+线段树)
  • 原文地址:https://www.cnblogs.com/sparkdev/p/8376747.html
Copyright © 2011-2022 走看看