在使用 PowerShell 的过程当中,发现它的异常处理并不像想象中的那么直观,因此在这里总结一下。数据库
经过 ThrowTerminatingError 触发的错误称为 Terminating Errors。本质上它是建立了一个异常,因此咱们可使用 catch 语句来捕获 Terminating Errors。所以 Terminating Errors 的另一个名字叫 Exceptions。默认状况下,Terminating Errors 不影响后面命令的执行!ui
把下面的代码保存到文件 exception.ps1 中:spa
# 下面的命令不存在 Get-TerminatingError Write-Host 'hello world'
而后执行脚本 exception.ps1:日志
注意最后输出的 "hello world",虽然执行过程当中出现了错误,可是错误后面的代码依然被执行了。
Terminating Errors 的特色是默认状况下你能够经过 catch 语句捕获它,从而控制代码的执行流。
把下面的代码保存到文件 exception.ps1 中:code
Try{ # 下面的命令不存在 Get-TerminatingError } Catch{ Write-Host 'got you' exit 1 } Write-Host 'hello world'
而后执行脚本 exception.ps1:blog
这样就好多了,咱们预测了代码中可能产生的问题,而且在发生了错误的状况下果断的结束了脚本的执行。ip
虽然 Terminating Errors 不能中断脚本的执行,可是却能够中断 pipeline 的执行。从 Terminating Errors 的文档中咱们能够看到,其内部抛出了 PipelineStoppedException 异常,而且 PowerShell 内部处理了这个异常。因此正在执行的 pipeline 会被中断掉,而后继续执行后面的其它代码。文档
经过 Write-Error 触发的错误称为 Non-Terminating Errors。本质上它只是把错误信息写入了输出流中而没有产生异常,因此默认状况下 catch 语句没法捕获 Non-Terminating Errors。默认状况下,Non-Terminating Errors 不影响后面命令的执行!workflow
把下面的代码保存到文件 exception.ps1 中:it
# 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,结果和上面是同样的,错误后面的代码依然会被执行。
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 语句是不完整的。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 是一个 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 中异常相关的概念。